istio
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
15package util16
17import (18"context"19"fmt"20"net/netip"21
22corev1 "k8s.io/api/core/v1"23"k8s.io/apimachinery/pkg/api/errors"24metav1 "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"istio.io/istio/pkg/util/sets"31)
32
33var annotationPatch = []byte(fmt.Sprintf(34`{"metadata":{"annotations":{"%s":"%s"}}}`,35constants.AmbientRedirection,36constants.AmbientRedirectionEnabled,37))38
39var annotationRemovePatch = []byte(fmt.Sprintf(40`{"metadata":{"annotations":{"%s":null}}}`,41constants.AmbientRedirection,42))43
44// TODO: we should use the upstream istio version of this function.
45// PodRedirectionEnabled determines if a pod should or should not be configured
46// to have traffic redirected thru the node proxy.
47func PodRedirectionEnabled(namespace *corev1.Namespace, pod *corev1.Pod) bool {48if namespace.GetLabels()[constants.DataplaneMode] != constants.DataplaneModeAmbient {49// Namespace does not have ambient mode enabled50return false51}52if podHasSidecar(pod) {53// Ztunnel and sidecar for a single pod is currently not supported; opt out.54return false55}56if pod.Annotations[constants.AmbientRedirection] == constants.AmbientRedirectionDisabled {57// Pod explicitly asked to not have redirection enabled58return false59}60return true61}
62
63func podHasSidecar(pod *corev1.Pod) bool {64if _, f := pod.GetAnnotations()[annotation.SidecarStatus.Name]; f {65return true66}67return false68}
69
70func IsZtunnelPod(systemNs string, pod *corev1.Pod) bool {71return pod.Namespace == systemNs && pod.GetLabels()["app"] == "ztunnel"72}
73
74func AnnotateEnrolledPod(client kubernetes.Interface, pod *metav1.ObjectMeta) error {75_, err := client.CoreV1().76Pods(pod.Namespace).77Patch(78context.Background(),79pod.Name,80types.MergePatchType,81annotationPatch,82metav1.PatchOptions{},83// Both "pods" and "pods/status" can mutate the metadata. However, pods/status is lower privilege, so we use that instead.84"status",85)86return err87}
88
89func AnnotateUnenrollPod(client kubernetes.Interface, pod *metav1.ObjectMeta) error {90if pod.Annotations[constants.AmbientRedirection] != constants.AmbientRedirectionEnabled {91return nil92}93// TODO: do not overwrite if already none94_, err := client.CoreV1().95Pods(pod.Namespace).96Patch(97context.Background(),98pod.Name,99types.MergePatchType,100annotationRemovePatch,101metav1.PatchOptions{},102// Both "pods" and "pods/status" can mutate the metadata. However, pods/status is lower privilege, so we use that instead.103"status",104)105if errors.IsNotFound(err) {106return nil107}108return err109}
110
111func GetEnvFromPod(pod *corev1.Pod, envName string) string {112for _, container := range pod.Spec.Containers {113for _, env := range container.Env {114if env.Name == envName {115return env.Value116}117}118}119return ""120}
121
122// GetUID is a nil safe UID accessor
123func GetUID(o *corev1.Pod) types.UID {124if o == nil {125return ""126}127return o.GetUID()128}
129
130func GetUniquePodUIDs(pods []*corev1.Pod) sets.Set[types.UID] {131uids := sets.New[types.UID]()132for _, pod := range pods {133uids.Insert(GetUID(pod))134}135return uids136}
137
138// Get any IPs currently assigned to the Pod.
139//
140// If 'PodIPs' exists, it is preferred (and should be guaranteed to contain the address in 'PodIP'),
141// otherwise fallback to 'PodIP'.
142//
143// Note that very early in the pod's lifecycle (before all the node CNI plugin invocations finish)
144// K8S may not have received the pod IPs yet, and may not report the pod as having any.
145func GetPodIPsIfPresent(pod *corev1.Pod) []netip.Addr {146var podIPs []netip.Addr147if len(pod.Status.PodIPs) != 0 {148for _, pip := range pod.Status.PodIPs {149ip := netip.MustParseAddr(pip.IP)150podIPs = append(podIPs, ip)151}152} else if len(pod.Status.PodIP) != 0 {153ip := netip.MustParseAddr(pod.Status.PodIP)154podIPs = append(podIPs, ip)155}156return podIPs157}
158