istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/util/podutil.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package util
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net/netip"
    21  
    22  	corev1 "k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/api/errors"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/types"
    26  	"k8s.io/client-go/kubernetes"
    27  
    28  	"istio.io/api/annotation"
    29  	"istio.io/istio/pkg/config/constants"
    30  )
    31  
    32  var annotationPatch = []byte(fmt.Sprintf(
    33  	`{"metadata":{"annotations":{"%s":"%s"}}}`,
    34  	constants.AmbientRedirection,
    35  	constants.AmbientRedirectionEnabled,
    36  ))
    37  
    38  var annotationRemovePatch = []byte(fmt.Sprintf(
    39  	`{"metadata":{"annotations":{"%s":null}}}`,
    40  	constants.AmbientRedirection,
    41  ))
    42  
    43  // PodRedirectionEnabled determines if a pod should or should not be configured
    44  // to have traffic redirected thru the node proxy.
    45  func PodRedirectionEnabled(namespace *corev1.Namespace, pod *corev1.Pod) bool {
    46  	if !(namespace.GetLabels()[constants.DataplaneModeLabel] == constants.DataplaneModeAmbient ||
    47  		pod.GetLabels()[constants.DataplaneModeLabel] == constants.DataplaneModeAmbient) {
    48  		// Neither namespace nor pod has ambient mode enabled
    49  		return false
    50  	}
    51  	if podHasSidecar(pod) {
    52  		// Ztunnel and sidecar for a single pod is currently not supported; opt out.
    53  		return false
    54  	}
    55  	if pod.GetLabels()[constants.DataplaneModeLabel] == constants.DataplaneModeNone {
    56  		// Pod explicitly asked to not have ambient redirection enabled
    57  		return false
    58  	}
    59  	return true
    60  }
    61  
    62  func podHasSidecar(pod *corev1.Pod) bool {
    63  	if _, f := pod.GetAnnotations()[annotation.SidecarStatus.Name]; f {
    64  		return true
    65  	}
    66  	return false
    67  }
    68  
    69  func IsZtunnelPod(systemNs string, pod *corev1.Pod) bool {
    70  	return pod.Namespace == systemNs && pod.GetLabels()["app"] == "ztunnel"
    71  }
    72  
    73  func AnnotateEnrolledPod(client kubernetes.Interface, pod *metav1.ObjectMeta) error {
    74  	_, err := client.CoreV1().
    75  		Pods(pod.Namespace).
    76  		Patch(
    77  			context.Background(),
    78  			pod.Name,
    79  			types.MergePatchType,
    80  			annotationPatch,
    81  			metav1.PatchOptions{},
    82  			// Both "pods" and "pods/status" can mutate the metadata. However, pods/status is lower privilege, so we use that instead.
    83  			"status",
    84  		)
    85  	return err
    86  }
    87  
    88  func AnnotateUnenrollPod(client kubernetes.Interface, pod *metav1.ObjectMeta) error {
    89  	if pod.Annotations[constants.AmbientRedirection] != constants.AmbientRedirectionEnabled {
    90  		return nil
    91  	}
    92  	// TODO: do not overwrite if already none
    93  	_, err := client.CoreV1().
    94  		Pods(pod.Namespace).
    95  		Patch(
    96  			context.Background(),
    97  			pod.Name,
    98  			types.MergePatchType,
    99  			annotationRemovePatch,
   100  			metav1.PatchOptions{},
   101  			// Both "pods" and "pods/status" can mutate the metadata. However, pods/status is lower privilege, so we use that instead.
   102  			"status",
   103  		)
   104  	if errors.IsNotFound(err) {
   105  		return nil
   106  	}
   107  	return err
   108  }
   109  
   110  // Get any IPs currently assigned to the Pod.
   111  //
   112  // If 'PodIPs' exists, it is preferred (and should be guaranteed to contain the address in 'PodIP'),
   113  // otherwise fallback to 'PodIP'.
   114  //
   115  // Note that very early in the pod's lifecycle (before all the node CNI plugin invocations finish)
   116  // K8S may not have received the pod IPs yet, and may not report the pod as having any.
   117  func GetPodIPsIfPresent(pod *corev1.Pod) []netip.Addr {
   118  	var podIPs []netip.Addr
   119  	if len(pod.Status.PodIPs) != 0 {
   120  		for _, pip := range pod.Status.PodIPs {
   121  			ip := netip.MustParseAddr(pip.IP)
   122  			podIPs = append(podIPs, ip)
   123  		}
   124  	} else if len(pod.Status.PodIP) != 0 {
   125  		ip := netip.MustParseAddr(pod.Status.PodIP)
   126  		podIPs = append(podIPs, ip)
   127  	}
   128  	return podIPs
   129  }