istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/kube/controller/ambient/helpers.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 ambient 16 17 import ( 18 "fmt" 19 "net/netip" 20 "strings" 21 22 v1 "k8s.io/api/core/v1" 23 24 "istio.io/istio/pilot/pkg/model" 25 "istio.io/istio/pkg/config/labels" 26 "istio.io/istio/pkg/workloadapi" 27 ) 28 29 // name format: <cluster>/<group>/<kind>/<namespace>/<name></section-name> 30 func (a *index) generatePodUID(p *v1.Pod) string { 31 return a.ClusterID.String() + "//" + "Pod/" + p.Namespace + "/" + p.Name 32 } 33 34 // name format: <cluster>/<group>/<kind>/<namespace>/<name></section-name> 35 // if the WorkloadEntry is inlined in the ServiceEntry, we may need section name. caller should use generateServiceEntryUID 36 func (a *index) generateWorkloadEntryUID(wkEntryNamespace, wkEntryName string) string { 37 return a.ClusterID.String() + "/networking.istio.io/WorkloadEntry/" + wkEntryNamespace + "/" + wkEntryName 38 } 39 40 // name format: <cluster>/<group>/<kind>/<namespace>/<name></section-name> 41 // section name should be the WE address, which needs to be stable across SE updates (it is assumed WE addresses are unique) 42 func (a *index) generateServiceEntryUID(svcEntryNamespace, svcEntryName, addr string) string { 43 return a.ClusterID.String() + "/networking.istio.io/ServiceEntry/" + svcEntryNamespace + "/" + svcEntryName + "/" + addr 44 } 45 46 func workloadToAddressInfo(w *workloadapi.Workload) model.AddressInfo { 47 return model.AddressInfo{ 48 Address: &workloadapi.Address{ 49 Type: &workloadapi.Address_Workload{ 50 Workload: w, 51 }, 52 }, 53 } 54 } 55 56 func modelWorkloadToAddressInfo(w model.WorkloadInfo) model.AddressInfo { 57 return workloadToAddressInfo(w.Workload) 58 } 59 60 func serviceToAddressInfo(s *workloadapi.Service) model.AddressInfo { 61 return model.AddressInfo{ 62 Address: &workloadapi.Address{ 63 Type: &workloadapi.Address_Service{ 64 Service: s, 65 }, 66 }, 67 } 68 } 69 70 func mustByteIPToString(b []byte) string { 71 ip, _ := netip.AddrFromSlice(b) // Address only comes from objects we create, so it must be valid 72 return ip.String() 73 } 74 75 func byteIPToAddr(b []byte) netip.Addr { 76 ip, _ := netip.AddrFromSlice(b) // Address only comes from objects we create, so it must be valid 77 return ip 78 } 79 80 func (a index) getWaypointAddress(w *Waypoint) *workloadapi.GatewayAddress { 81 // probably overly cautious... I don't think the ambient index impl counts something with zero addresses as waypoint 82 if w != nil && len(w.Addresses) >= 1 { 83 return &workloadapi.GatewayAddress{ 84 Destination: &workloadapi.GatewayAddress_Address{ 85 // probably use from Cidr instead? 86 Address: a.toNetworkAddressFromIP(w.Addresses[0]), 87 }, 88 // TODO: look up the HBONE port instead of hardcoding it 89 HboneMtlsPort: 15008, 90 } 91 } 92 return nil 93 } 94 95 func (a *index) toNetworkAddress(vip string) (*workloadapi.NetworkAddress, error) { 96 ip, err := netip.ParseAddr(vip) 97 if err != nil { 98 return nil, fmt.Errorf("parse %v: %v", vip, err) 99 } 100 return &workloadapi.NetworkAddress{ 101 Network: a.Network(vip, make(labels.Instance, 0)).String(), 102 Address: ip.AsSlice(), 103 }, nil 104 } 105 106 func (a *index) toNetworkAddressFromIP(ip netip.Addr) *workloadapi.NetworkAddress { 107 return &workloadapi.NetworkAddress{ 108 Network: a.Network(ip.String(), make(labels.Instance, 0)).String(), 109 Address: ip.AsSlice(), 110 } 111 } 112 113 func (a *index) toNetworkAddressFromCidr(vip string) (*workloadapi.NetworkAddress, error) { 114 ip, err := parseCidrOrIP(vip) 115 if err != nil { 116 return nil, err 117 } 118 return &workloadapi.NetworkAddress{ 119 Network: a.Network(vip, make(labels.Instance, 0)).String(), 120 Address: ip.AsSlice(), 121 }, nil 122 } 123 124 // parseCidrOrIP parses an IP or a CIDR of a exactly 1 IP (e.g. /32). 125 // This is to support ServiceEntry which supports CIDRs, but we don't currently support more than 1 IP 126 func parseCidrOrIP(ip string) (netip.Addr, error) { 127 if strings.Contains(ip, "/") { 128 prefix, err := netip.ParsePrefix(ip) 129 if err != nil { 130 return netip.Addr{}, err 131 } 132 if !prefix.IsSingleIP() { 133 return netip.Addr{}, fmt.Errorf("only single IP CIDR is allowed") 134 } 135 return prefix.Addr(), nil 136 } 137 return netip.ParseAddr(ip) 138 } 139 140 func AppendNonNil[T any](data []T, i *T) []T { 141 if i != nil { 142 data = append(data, *i) 143 } 144 return data 145 } 146 147 func IsPodRunning(pod *v1.Pod) bool { 148 return pod.Status.Phase == v1.PodRunning 149 } 150 151 func IsPodPending(pod *v1.Pod) bool { 152 return pod.Status.Phase == v1.PodPending 153 } 154 155 // IsPodReady is copied from kubernetes/pkg/api/v1/pod/utils.go 156 func IsPodReady(pod *v1.Pod) bool { 157 return IsPodReadyConditionTrue(pod.Status) 158 } 159 160 // IsPodReadyConditionTrue returns true if a pod is ready; false otherwise. 161 func IsPodReadyConditionTrue(status v1.PodStatus) bool { 162 condition := GetPodReadyCondition(status) 163 return condition != nil && condition.Status == v1.ConditionTrue 164 } 165 166 func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition { 167 _, condition := GetPodCondition(&status, v1.PodReady) 168 return condition 169 } 170 171 func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 172 if status == nil { 173 return -1, nil 174 } 175 return GetPodConditionFromList(status.Conditions, conditionType) 176 } 177 178 // GetPodConditionFromList extracts the provided condition from the given list of condition and 179 // returns the index of the condition and the condition. Returns -1 and nil if the condition is not present. 180 func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) { 181 if conditions == nil { 182 return -1, nil 183 } 184 for i := range conditions { 185 if conditions[i].Type == conditionType { 186 return i, &conditions[i] 187 } 188 } 189 return -1, nil 190 } 191 192 func FindPortName(pod *v1.Pod, name string) (int32, bool) { 193 for _, container := range pod.Spec.Containers { 194 for _, port := range container.Ports { 195 if port.Name == name && port.Protocol == v1.ProtocolTCP { 196 return port.ContainerPort, true 197 } 198 } 199 } 200 return 0, false 201 } 202 203 func namespacedHostname(namespace, hostname string) string { 204 return namespace + "/" + hostname 205 } 206 207 func networkAddressFromWorkload(wl model.WorkloadInfo) []networkAddress { 208 networkAddrs := make([]networkAddress, 0, len(wl.Addresses)) 209 for _, addr := range wl.Addresses { 210 // mustByteIPToString is ok since this is from our IP constructed 211 networkAddrs = append(networkAddrs, networkAddress{network: wl.Network, ip: mustByteIPToString(addr)}) 212 } 213 return networkAddrs 214 } 215 216 func networkAddressFromService(s model.ServiceInfo) []networkAddress { 217 networkAddrs := make([]networkAddress, 0, len(s.Addresses)) 218 for _, addr := range s.Addresses { 219 // mustByteIPToString is ok since this is from our IP constructed 220 networkAddrs = append(networkAddrs, networkAddress{network: addr.Network, ip: mustByteIPToString(addr.Address)}) 221 } 222 return networkAddrs 223 } 224 225 // internal object used for indexing in ambientindex maps 226 type networkAddress struct { 227 network string 228 ip string 229 } 230 231 func (n *networkAddress) String() string { 232 return n.network + "/" + n.ip 233 }