1
16
17 package pod
18
19 import (
20 "context"
21 "fmt"
22 "net"
23 "net/http"
24 "net/url"
25 "strconv"
26 "strings"
27 "time"
28
29 apiv1 "k8s.io/api/core/v1"
30 apiequality "k8s.io/apimachinery/pkg/api/equality"
31 "k8s.io/apimachinery/pkg/api/errors"
32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/apimachinery/pkg/fields"
34 "k8s.io/apimachinery/pkg/labels"
35 "k8s.io/apimachinery/pkg/runtime"
36 "k8s.io/apimachinery/pkg/types"
37 utilnet "k8s.io/apimachinery/pkg/util/net"
38 utilvalidation "k8s.io/apimachinery/pkg/util/validation"
39 "k8s.io/apimachinery/pkg/util/validation/field"
40 "k8s.io/apiserver/pkg/registry/generic"
41 "k8s.io/apiserver/pkg/storage"
42 "k8s.io/apiserver/pkg/storage/names"
43 utilfeature "k8s.io/apiserver/pkg/util/feature"
44 "k8s.io/apiserver/pkg/warning"
45 "k8s.io/client-go/tools/cache"
46 "k8s.io/kubernetes/pkg/api/legacyscheme"
47 podutil "k8s.io/kubernetes/pkg/api/pod"
48 api "k8s.io/kubernetes/pkg/apis/core"
49 "k8s.io/kubernetes/pkg/apis/core/helper/qos"
50 corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
51 "k8s.io/kubernetes/pkg/features"
52 "k8s.io/kubernetes/pkg/kubelet/client"
53 netutils "k8s.io/utils/net"
54 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
55 )
56
57
58 type podStrategy struct {
59 runtime.ObjectTyper
60 names.NameGenerator
61 }
62
63
64
65 var Strategy = podStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
66
67
68 func (podStrategy) NamespaceScoped() bool {
69 return true
70 }
71
72
73
74 func (podStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
75 fields := map[fieldpath.APIVersion]*fieldpath.Set{
76 "v1": fieldpath.NewSet(
77 fieldpath.MakePathOrDie("status"),
78 ),
79 }
80
81 return fields
82 }
83
84
85 func (podStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
86 pod := obj.(*api.Pod)
87 pod.Status = api.PodStatus{
88 Phase: api.PodPending,
89 QOSClass: qos.GetPodQOS(pod),
90 }
91
92 podutil.DropDisabledPodFields(pod, nil)
93
94 applySchedulingGatedCondition(pod)
95 mutatePodAffinity(pod)
96 applyAppArmorVersionSkew(ctx, pod)
97 }
98
99
100 func (podStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
101 newPod := obj.(*api.Pod)
102 oldPod := old.(*api.Pod)
103 newPod.Status = oldPod.Status
104
105 if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
106
107
108
109
110 podutil.MarkPodProposedForResize(oldPod, newPod)
111 }
112
113 podutil.DropDisabledPodFields(newPod, oldPod)
114 }
115
116
117 func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
118 pod := obj.(*api.Pod)
119 opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, nil, &pod.ObjectMeta, nil)
120 opts.ResourceIsPod = true
121 return corevalidation.ValidatePodCreate(pod, opts)
122 }
123
124
125 func (podStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
126 newPod := obj.(*api.Pod)
127 var warnings []string
128 if msgs := utilvalidation.IsDNS1123Label(newPod.Name); len(msgs) != 0 {
129 warnings = append(warnings, fmt.Sprintf("metadata.name: this is used in the Pod's hostname, which can result in surprising behavior; a DNS label is recommended: %v", msgs))
130 }
131 warnings = append(warnings, podutil.GetWarningsForPod(ctx, newPod, nil)...)
132 return warnings
133 }
134
135
136 func (podStrategy) Canonicalize(obj runtime.Object) {
137 }
138
139
140 func (podStrategy) AllowCreateOnUpdate() bool {
141 return false
142 }
143
144
145 func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
146
147 pod := obj.(*api.Pod)
148 oldPod := old.(*api.Pod)
149 opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
150 opts.ResourceIsPod = true
151 return corevalidation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
152 }
153
154
155 func (podStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
156
157
158 return nil
159 }
160
161
162 func (podStrategy) AllowUnconditionalUpdate() bool {
163 return true
164 }
165
166
167
168 func (podStrategy) CheckGracefulDelete(ctx context.Context, obj runtime.Object, options *metav1.DeleteOptions) bool {
169 if options == nil {
170 return false
171 }
172 pod := obj.(*api.Pod)
173 period := int64(0)
174
175 if options.GracePeriodSeconds != nil {
176 period = *options.GracePeriodSeconds
177 } else {
178
179 if pod.Spec.TerminationGracePeriodSeconds != nil {
180 period = *pod.Spec.TerminationGracePeriodSeconds
181 }
182 }
183
184 if len(pod.Spec.NodeName) == 0 {
185 period = 0
186 }
187
188 if pod.Status.Phase == api.PodFailed || pod.Status.Phase == api.PodSucceeded {
189 period = 0
190 }
191
192 if period < 0 {
193 period = 1
194 }
195
196
197 options.GracePeriodSeconds = &period
198 return true
199 }
200
201 type podStatusStrategy struct {
202 podStrategy
203 }
204
205
206 var StatusStrategy = podStatusStrategy{Strategy}
207
208
209
210 func (podStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
211 return map[fieldpath.APIVersion]*fieldpath.Set{
212 "v1": fieldpath.NewSet(
213 fieldpath.MakePathOrDie("spec"),
214 fieldpath.MakePathOrDie("metadata", "deletionTimestamp"),
215 fieldpath.MakePathOrDie("metadata", "ownerReferences"),
216 ),
217 }
218 }
219
220 func (podStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
221 newPod := obj.(*api.Pod)
222 oldPod := old.(*api.Pod)
223 newPod.Spec = oldPod.Spec
224 newPod.DeletionTimestamp = nil
225
226
227
228 newPod.OwnerReferences = oldPod.OwnerReferences
229 }
230
231 func (podStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
232 pod := obj.(*api.Pod)
233 oldPod := old.(*api.Pod)
234 opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
235 opts.ResourceIsPod = true
236
237 return corevalidation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
238 }
239
240
241 func (podStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
242 return nil
243 }
244
245 type podEphemeralContainersStrategy struct {
246 podStrategy
247 }
248
249
250 var EphemeralContainersStrategy = podEphemeralContainersStrategy{Strategy}
251
252
253 func dropNonEphemeralContainerUpdates(newPod, oldPod *api.Pod) *api.Pod {
254 pod := oldPod.DeepCopy()
255 pod.Name = newPod.Name
256 pod.Namespace = newPod.Namespace
257 pod.ResourceVersion = newPod.ResourceVersion
258 pod.UID = newPod.UID
259 pod.Spec.EphemeralContainers = newPod.Spec.EphemeralContainers
260 return pod
261 }
262
263 func (podEphemeralContainersStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
264 newPod := obj.(*api.Pod)
265 oldPod := old.(*api.Pod)
266
267 *newPod = *dropNonEphemeralContainerUpdates(newPod, oldPod)
268 podutil.DropDisabledPodFields(newPod, oldPod)
269 }
270
271 func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
272 newPod := obj.(*api.Pod)
273 oldPod := old.(*api.Pod)
274 opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&newPod.Spec, &oldPod.Spec, &newPod.ObjectMeta, &oldPod.ObjectMeta)
275 opts.ResourceIsPod = true
276 return corevalidation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
277 }
278
279
280 func (podEphemeralContainersStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
281 return nil
282 }
283
284
285 func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
286 pod, ok := obj.(*api.Pod)
287 if !ok {
288 return nil, nil, fmt.Errorf("not a pod")
289 }
290 return labels.Set(pod.ObjectMeta.Labels), ToSelectableFields(pod), nil
291 }
292
293
294 func MatchPod(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
295 var indexFields = []string{"spec.nodeName"}
296 if utilfeature.DefaultFeatureGate.Enabled(features.StorageNamespaceIndex) {
297 indexFields = append(indexFields, "metadata.namespace")
298 }
299 return storage.SelectionPredicate{
300 Label: label,
301 Field: field,
302 GetAttrs: GetAttrs,
303 IndexFields: indexFields,
304 }
305 }
306
307
308 func NodeNameTriggerFunc(obj runtime.Object) string {
309 return obj.(*api.Pod).Spec.NodeName
310 }
311
312
313 func NodeNameIndexFunc(obj interface{}) ([]string, error) {
314 pod, ok := obj.(*api.Pod)
315 if !ok {
316 return nil, fmt.Errorf("not a pod")
317 }
318 return []string{pod.Spec.NodeName}, nil
319 }
320
321
322 func NamespaceIndexFunc(obj interface{}) ([]string, error) {
323 pod, ok := obj.(*api.Pod)
324 if !ok {
325 return nil, fmt.Errorf("not a pod")
326 }
327 return []string{pod.Namespace}, nil
328 }
329
330
331 func Indexers() *cache.Indexers {
332 var indexers = cache.Indexers{
333 storage.FieldIndex("spec.nodeName"): NodeNameIndexFunc,
334 }
335 if utilfeature.DefaultFeatureGate.Enabled(features.StorageNamespaceIndex) {
336 indexers[storage.FieldIndex("metadata.namespace")] = NamespaceIndexFunc
337 }
338 return &indexers
339 }
340
341
342
343 func ToSelectableFields(pod *api.Pod) fields.Set {
344
345
346
347
348 podSpecificFieldsSet := make(fields.Set, 10)
349 podSpecificFieldsSet["spec.nodeName"] = pod.Spec.NodeName
350 podSpecificFieldsSet["spec.restartPolicy"] = string(pod.Spec.RestartPolicy)
351 podSpecificFieldsSet["spec.schedulerName"] = string(pod.Spec.SchedulerName)
352 podSpecificFieldsSet["spec.serviceAccountName"] = string(pod.Spec.ServiceAccountName)
353 if pod.Spec.SecurityContext != nil {
354 podSpecificFieldsSet["spec.hostNetwork"] = strconv.FormatBool(pod.Spec.SecurityContext.HostNetwork)
355 } else {
356
357 podSpecificFieldsSet["spec.hostNetwork"] = strconv.FormatBool(false)
358 }
359 podSpecificFieldsSet["status.phase"] = string(pod.Status.Phase)
360
361 podIP := ""
362 if len(pod.Status.PodIPs) > 0 {
363 podIP = string(pod.Status.PodIPs[0].IP)
364 }
365 podSpecificFieldsSet["status.podIP"] = podIP
366 podSpecificFieldsSet["status.nominatedNodeName"] = string(pod.Status.NominatedNodeName)
367 return generic.AddObjectMetaFieldsSet(podSpecificFieldsSet, &pod.ObjectMeta, true)
368 }
369
370
371 type ResourceGetter interface {
372 Get(context.Context, string, *metav1.GetOptions) (runtime.Object, error)
373 }
374
375 func getPod(ctx context.Context, getter ResourceGetter, name string) (*api.Pod, error) {
376 obj, err := getter.Get(ctx, name, &metav1.GetOptions{})
377 if err != nil {
378 return nil, err
379 }
380 pod := obj.(*api.Pod)
381 if pod == nil {
382 return nil, fmt.Errorf("Unexpected object type: %#v", pod)
383 }
384 return pod, nil
385 }
386
387
388 func getPodIP(pod *api.Pod) string {
389 if pod == nil {
390 return ""
391 }
392 if len(pod.Status.PodIPs) > 0 {
393 return pod.Status.PodIPs[0].IP
394 }
395
396 return ""
397 }
398
399
400 func ResourceLocation(ctx context.Context, getter ResourceGetter, rt http.RoundTripper, id string) (*url.URL, http.RoundTripper, error) {
401
402
403 scheme, name, port, valid := utilnet.SplitSchemeNamePort(id)
404 if !valid {
405 return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid pod request %q", id))
406 }
407
408 pod, err := getPod(ctx, getter, name)
409 if err != nil {
410 return nil, nil, err
411 }
412
413
414 if port == "" {
415 for i := range pod.Spec.Containers {
416 if len(pod.Spec.Containers[i].Ports) > 0 {
417 port = fmt.Sprintf("%d", pod.Spec.Containers[i].Ports[0].ContainerPort)
418 break
419 }
420 }
421 }
422 podIP := getPodIP(pod)
423 if ip := netutils.ParseIPSloppy(podIP); ip == nil || !ip.IsGlobalUnicast() {
424 return nil, nil, errors.NewBadRequest("address not allowed")
425 }
426
427 loc := &url.URL{
428 Scheme: scheme,
429 }
430 if port == "" {
431
432
433 if strings.Contains(podIP, ":") {
434 loc.Host = "[" + podIP + "]"
435 } else {
436 loc.Host = podIP
437 }
438 } else {
439 loc.Host = net.JoinHostPort(podIP, port)
440 }
441 return loc, rt, nil
442 }
443
444
445
446 func LogLocation(
447 ctx context.Context, getter ResourceGetter,
448 connInfo client.ConnectionInfoGetter,
449 name string,
450 opts *api.PodLogOptions,
451 ) (*url.URL, http.RoundTripper, error) {
452 pod, err := getPod(ctx, getter, name)
453 if err != nil {
454 return nil, nil, err
455 }
456
457
458
459 container := opts.Container
460 container, err = validateContainer(container, pod)
461 if err != nil {
462 return nil, nil, err
463 }
464 nodeName := types.NodeName(pod.Spec.NodeName)
465 if len(nodeName) == 0 {
466
467 return nil, nil, nil
468 }
469 nodeInfo, err := connInfo.GetConnectionInfo(ctx, nodeName)
470 if err != nil {
471 return nil, nil, err
472 }
473 params := url.Values{}
474 if opts.Follow {
475 params.Add("follow", "true")
476 }
477 if opts.Previous {
478 params.Add("previous", "true")
479 }
480 if opts.Timestamps {
481 params.Add("timestamps", "true")
482 }
483 if opts.SinceSeconds != nil {
484 params.Add("sinceSeconds", strconv.FormatInt(*opts.SinceSeconds, 10))
485 }
486 if opts.SinceTime != nil {
487 params.Add("sinceTime", opts.SinceTime.Format(time.RFC3339))
488 }
489 if opts.TailLines != nil {
490 params.Add("tailLines", strconv.FormatInt(*opts.TailLines, 10))
491 }
492 if opts.LimitBytes != nil {
493 params.Add("limitBytes", strconv.FormatInt(*opts.LimitBytes, 10))
494 }
495 loc := &url.URL{
496 Scheme: nodeInfo.Scheme,
497 Host: net.JoinHostPort(nodeInfo.Hostname, nodeInfo.Port),
498 Path: fmt.Sprintf("/containerLogs/%s/%s/%s", pod.Namespace, pod.Name, container),
499 RawQuery: params.Encode(),
500 }
501
502 if opts.InsecureSkipTLSVerifyBackend {
503 return loc, nodeInfo.InsecureSkipTLSVerifyTransport, nil
504 }
505 return loc, nodeInfo.Transport, nil
506 }
507
508 func podHasContainerWithName(pod *api.Pod, containerName string) bool {
509 var hasContainer bool
510 podutil.VisitContainers(&pod.Spec, podutil.AllFeatureEnabledContainers(), func(c *api.Container, containerType podutil.ContainerType) bool {
511 if c.Name == containerName {
512 hasContainer = true
513 return false
514 }
515 return true
516 })
517 return hasContainer
518 }
519
520 func streamParams(params url.Values, opts runtime.Object) error {
521 switch opts := opts.(type) {
522 case *api.PodExecOptions:
523 if opts.Stdin {
524 params.Add(api.ExecStdinParam, "1")
525 }
526 if opts.Stdout {
527 params.Add(api.ExecStdoutParam, "1")
528 }
529 if opts.Stderr {
530 params.Add(api.ExecStderrParam, "1")
531 }
532 if opts.TTY {
533 params.Add(api.ExecTTYParam, "1")
534 }
535 for _, c := range opts.Command {
536 params.Add("command", c)
537 }
538 case *api.PodAttachOptions:
539 if opts.Stdin {
540 params.Add(api.ExecStdinParam, "1")
541 }
542 if opts.Stdout {
543 params.Add(api.ExecStdoutParam, "1")
544 }
545 if opts.Stderr {
546 params.Add(api.ExecStderrParam, "1")
547 }
548 if opts.TTY {
549 params.Add(api.ExecTTYParam, "1")
550 }
551 case *api.PodPortForwardOptions:
552 if len(opts.Ports) > 0 {
553 ports := make([]string, len(opts.Ports))
554 for i, p := range opts.Ports {
555 ports[i] = strconv.FormatInt(int64(p), 10)
556 }
557 params.Add(api.PortHeader, strings.Join(ports, ","))
558 }
559 default:
560 return fmt.Errorf("Unknown object for streaming: %v", opts)
561 }
562 return nil
563 }
564
565
566
567 func AttachLocation(
568 ctx context.Context,
569 getter ResourceGetter,
570 connInfo client.ConnectionInfoGetter,
571 name string,
572 opts *api.PodAttachOptions,
573 ) (*url.URL, http.RoundTripper, error) {
574 return streamLocation(ctx, getter, connInfo, name, opts, opts.Container, "attach")
575 }
576
577
578
579 func ExecLocation(
580 ctx context.Context,
581 getter ResourceGetter,
582 connInfo client.ConnectionInfoGetter,
583 name string,
584 opts *api.PodExecOptions,
585 ) (*url.URL, http.RoundTripper, error) {
586 return streamLocation(ctx, getter, connInfo, name, opts, opts.Container, "exec")
587 }
588
589 func streamLocation(
590 ctx context.Context,
591 getter ResourceGetter,
592 connInfo client.ConnectionInfoGetter,
593 name string,
594 opts runtime.Object,
595 container,
596 path string,
597 ) (*url.URL, http.RoundTripper, error) {
598 pod, err := getPod(ctx, getter, name)
599 if err != nil {
600 return nil, nil, err
601 }
602
603
604
605 container, err = validateContainer(container, pod)
606 if err != nil {
607 return nil, nil, err
608 }
609
610 nodeName := types.NodeName(pod.Spec.NodeName)
611 if len(nodeName) == 0 {
612
613 return nil, nil, errors.NewBadRequest(fmt.Sprintf("pod %s does not have a host assigned", name))
614 }
615 nodeInfo, err := connInfo.GetConnectionInfo(ctx, nodeName)
616 if err != nil {
617 return nil, nil, err
618 }
619 params := url.Values{}
620 if err := streamParams(params, opts); err != nil {
621 return nil, nil, err
622 }
623 loc := &url.URL{
624 Scheme: nodeInfo.Scheme,
625 Host: net.JoinHostPort(nodeInfo.Hostname, nodeInfo.Port),
626 Path: fmt.Sprintf("/%s/%s/%s/%s", path, pod.Namespace, pod.Name, container),
627 RawQuery: params.Encode(),
628 }
629 return loc, nodeInfo.Transport, nil
630 }
631
632
633 func PortForwardLocation(
634 ctx context.Context,
635 getter ResourceGetter,
636 connInfo client.ConnectionInfoGetter,
637 name string,
638 opts *api.PodPortForwardOptions,
639 ) (*url.URL, http.RoundTripper, error) {
640 pod, err := getPod(ctx, getter, name)
641 if err != nil {
642 return nil, nil, err
643 }
644
645 nodeName := types.NodeName(pod.Spec.NodeName)
646 if len(nodeName) == 0 {
647
648 return nil, nil, errors.NewBadRequest(fmt.Sprintf("pod %s does not have a host assigned", name))
649 }
650 nodeInfo, err := connInfo.GetConnectionInfo(ctx, nodeName)
651 if err != nil {
652 return nil, nil, err
653 }
654 params := url.Values{}
655 if err := streamParams(params, opts); err != nil {
656 return nil, nil, err
657 }
658 loc := &url.URL{
659 Scheme: nodeInfo.Scheme,
660 Host: net.JoinHostPort(nodeInfo.Hostname, nodeInfo.Port),
661 Path: fmt.Sprintf("/portForward/%s/%s", pod.Namespace, pod.Name),
662 RawQuery: params.Encode(),
663 }
664 return loc, nodeInfo.Transport, nil
665 }
666
667
668 func validateContainer(container string, pod *api.Pod) (string, error) {
669 if len(container) == 0 {
670 switch len(pod.Spec.Containers) {
671 case 1:
672 container = pod.Spec.Containers[0].Name
673 case 0:
674 return "", errors.NewBadRequest(fmt.Sprintf("a container name must be specified for pod %s", pod.Name))
675 default:
676 var containerNames []string
677 podutil.VisitContainers(&pod.Spec, podutil.AllFeatureEnabledContainers(), func(c *api.Container, containerType podutil.ContainerType) bool {
678 containerNames = append(containerNames, c.Name)
679 return true
680 })
681 errStr := fmt.Sprintf("a container name must be specified for pod %s, choose one of: %s", pod.Name, containerNames)
682 return "", errors.NewBadRequest(errStr)
683 }
684 } else {
685 if !podHasContainerWithName(pod, container) {
686 return "", errors.NewBadRequest(fmt.Sprintf("container %s is not valid for pod %s", container, pod.Name))
687 }
688 }
689
690 return container, nil
691 }
692
693
694
695 func applyLabelKeysToLabelSelector(labelSelector *metav1.LabelSelector, labelKeys []string, operator metav1.LabelSelectorOperator, podLabels map[string]string) {
696 for _, key := range labelKeys {
697 if value, ok := podLabels[key]; ok {
698 labelSelector.MatchExpressions = append(labelSelector.MatchExpressions, metav1.LabelSelectorRequirement{
699 Key: key,
700 Operator: operator,
701 Values: []string{value},
702 })
703 }
704 }
705 }
706
707
708
709
710
711 func applyMatchLabelKeysAndMismatchLabelKeys(term *api.PodAffinityTerm, label map[string]string) {
712 if (len(term.MatchLabelKeys) == 0 && len(term.MismatchLabelKeys) == 0) || term.LabelSelector == nil {
713
714 return
715 }
716
717 applyLabelKeysToLabelSelector(term.LabelSelector, term.MatchLabelKeys, metav1.LabelSelectorOpIn, label)
718 applyLabelKeysToLabelSelector(term.LabelSelector, term.MismatchLabelKeys, metav1.LabelSelectorOpNotIn, label)
719 }
720
721 func mutatePodAffinity(pod *api.Pod) {
722 if !utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodAffinity) || pod.Spec.Affinity == nil {
723 return
724 }
725 if affinity := pod.Spec.Affinity.PodAffinity; affinity != nil {
726 for i := range affinity.PreferredDuringSchedulingIgnoredDuringExecution {
727 applyMatchLabelKeysAndMismatchLabelKeys(&affinity.PreferredDuringSchedulingIgnoredDuringExecution[i].PodAffinityTerm, pod.Labels)
728 }
729 for i := range affinity.RequiredDuringSchedulingIgnoredDuringExecution {
730 applyMatchLabelKeysAndMismatchLabelKeys(&affinity.RequiredDuringSchedulingIgnoredDuringExecution[i], pod.Labels)
731 }
732 }
733 if affinity := pod.Spec.Affinity.PodAntiAffinity; affinity != nil {
734 for i := range affinity.PreferredDuringSchedulingIgnoredDuringExecution {
735 applyMatchLabelKeysAndMismatchLabelKeys(&affinity.PreferredDuringSchedulingIgnoredDuringExecution[i].PodAffinityTerm, pod.Labels)
736 }
737 for i := range affinity.RequiredDuringSchedulingIgnoredDuringExecution {
738 applyMatchLabelKeysAndMismatchLabelKeys(&affinity.RequiredDuringSchedulingIgnoredDuringExecution[i], pod.Labels)
739 }
740 }
741 }
742
743
744
745 func applySchedulingGatedCondition(pod *api.Pod) {
746 if len(pod.Spec.SchedulingGates) == 0 {
747 return
748 }
749
750
751 for _, condition := range pod.Status.Conditions {
752 if condition.Type == api.PodScheduled {
753 return
754 }
755 }
756
757 pod.Status.Conditions = append(pod.Status.Conditions, api.PodCondition{
758 Type: api.PodScheduled,
759 Status: api.ConditionFalse,
760 Reason: apiv1.PodReasonSchedulingGated,
761 Message: "Scheduling is blocked due to non-empty scheduling gates",
762 })
763 }
764
765
766
767 func applyAppArmorVersionSkew(ctx context.Context, pod *api.Pod) {
768 if !utilfeature.DefaultFeatureGate.Enabled(features.AppArmorFields) {
769 return
770 }
771
772 if pod.Spec.OS != nil && pod.Spec.OS.Name == api.Windows {
773 return
774 }
775
776 var podProfile *api.AppArmorProfile
777 if pod.Spec.SecurityContext != nil {
778 podProfile = pod.Spec.SecurityContext.AppArmorProfile
779 }
780
781
782 podutil.VisitContainers(&pod.Spec, podutil.AllFeatureEnabledContainers(),
783 func(ctr *api.Container, _ podutil.ContainerType) bool {
784
785 key := api.DeprecatedAppArmorAnnotationKeyPrefix + ctr.Name
786 annotation, hasAnnotation := pod.Annotations[key]
787
788 var containerProfile *api.AppArmorProfile
789 if ctr.SecurityContext != nil {
790 containerProfile = ctr.SecurityContext.AppArmorProfile
791 }
792
793
794 if !hasAnnotation {
795 newAnnotation := ""
796 if containerProfile != nil {
797 newAnnotation = appArmorAnnotationForField(containerProfile)
798 } else if podProfile != nil {
799 newAnnotation = appArmorAnnotationForField(podProfile)
800 }
801
802 if newAnnotation != "" {
803 if pod.Annotations == nil {
804 pod.Annotations = map[string]string{}
805 }
806 pod.Annotations[key] = newAnnotation
807 }
808 } else if containerProfile == nil {
809 newField := apparmorFieldForAnnotation(annotation)
810 if errs := corevalidation.ValidateAppArmorProfileField(newField, &field.Path{}); len(errs) > 0 {
811
812 newField = nil
813 }
814
815
816 deprecationWarning := newField == nil
817
818
819 if newField != nil && !apiequality.Semantic.DeepEqual(newField, podProfile) {
820 if ctr.SecurityContext == nil {
821 ctr.SecurityContext = &api.SecurityContext{}
822 }
823 ctr.SecurityContext.AppArmorProfile = newField
824
825 deprecationWarning = true
826 }
827
828 if deprecationWarning {
829
830
831
832 fldPath := field.NewPath("metadata", "annotations").Key(key)
833 warning.AddWarning(ctx, "", fmt.Sprintf(`%s: deprecated since v1.30; use the "appArmorProfile" field instead`, fldPath))
834 }
835 }
836
837 return true
838 })
839 }
840
841
842
843 func appArmorAnnotationForField(field *api.AppArmorProfile) string {
844
845
846
847 switch field.Type {
848 case api.AppArmorProfileTypeUnconfined:
849 return api.DeprecatedAppArmorAnnotationValueUnconfined
850
851 case api.AppArmorProfileTypeRuntimeDefault:
852 return api.DeprecatedAppArmorAnnotationValueRuntimeDefault
853
854 case api.AppArmorProfileTypeLocalhost:
855 if field.LocalhostProfile != nil {
856 return api.DeprecatedAppArmorAnnotationValueLocalhostPrefix + *field.LocalhostProfile
857 }
858 }
859
860
861
862
863 return ""
864 }
865
866
867
868 func apparmorFieldForAnnotation(annotation string) *api.AppArmorProfile {
869 if annotation == api.DeprecatedAppArmorAnnotationValueUnconfined {
870 return &api.AppArmorProfile{Type: api.AppArmorProfileTypeUnconfined}
871 }
872
873 if annotation == api.DeprecatedAppArmorAnnotationValueRuntimeDefault {
874 return &api.AppArmorProfile{Type: api.AppArmorProfileTypeRuntimeDefault}
875 }
876
877 if strings.HasPrefix(annotation, api.DeprecatedAppArmorAnnotationValueLocalhostPrefix) {
878 localhostProfile := strings.TrimPrefix(annotation, api.DeprecatedAppArmorAnnotationValueLocalhostPrefix)
879 if localhostProfile != "" {
880 return &api.AppArmorProfile{
881 Type: api.AppArmorProfileTypeLocalhost,
882 LocalhostProfile: &localhostProfile,
883 }
884 }
885 }
886
887
888
889 return nil
890 }
891
View as plain text