istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/kube/controller/util.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 controller 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "strings" 21 22 v1 "k8s.io/api/core/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 klabels "k8s.io/apimachinery/pkg/labels" 25 "k8s.io/apimachinery/pkg/selection" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/apimachinery/pkg/util/intstr" 28 "k8s.io/apimachinery/pkg/util/validation/field" 29 30 "istio.io/api/annotation" 31 "istio.io/istio/pilot/pkg/model" 32 "istio.io/istio/pkg/config" 33 "istio.io/istio/pkg/config/constants" 34 "istio.io/istio/pkg/config/host" 35 "istio.io/istio/pkg/config/labels" 36 ) 37 38 func getLabelValue(metadata metav1.ObjectMeta, label string, fallBackLabel string) string { 39 metaLabels := metadata.GetLabels() 40 val := metaLabels[label] 41 if val != "" { 42 return val 43 } 44 45 return metaLabels[fallBackLabel] 46 } 47 48 // Forked from Kubernetes k8s.io/kubernetes/pkg/api/v1/pod 49 // FindPort locates the container port for the given pod and portName. If the 50 // targetPort is a number, use that. If the targetPort is a string, look that 51 // string up in all named ports in all containers in the target pod. If no 52 // match is found, fail. 53 func FindPort(pod *v1.Pod, svcPort *v1.ServicePort) (int, error) { 54 portName := svcPort.TargetPort 55 switch portName.Type { 56 case intstr.String: 57 name := portName.StrVal 58 for _, container := range pod.Spec.Containers { 59 for _, port := range container.Ports { 60 if port.Name == name && port.Protocol == svcPort.Protocol { 61 return int(port.ContainerPort), nil 62 } 63 } 64 } 65 case intstr.Int: 66 return portName.IntValue(), nil 67 } 68 69 return 0, fmt.Errorf("no suitable port for manifest: %s", pod.UID) 70 } 71 72 // findPortFromMetadata resolves the TargetPort of a Service Port, by reading the Pod spec. 73 func findPortFromMetadata(svcPort v1.ServicePort, podPorts []model.PodPort) (int, error) { 74 target := svcPort.TargetPort 75 76 switch target.Type { 77 case intstr.String: 78 name := target.StrVal 79 for _, port := range podPorts { 80 if port.Name == name && port.Protocol == string(svcPort.Protocol) { 81 return port.ContainerPort, nil 82 } 83 } 84 case intstr.Int: 85 // For a direct reference we can just return the port number 86 return target.IntValue(), nil 87 } 88 89 return 0, fmt.Errorf("no matching port found for %+v", svcPort) 90 } 91 92 type serviceTargetPort struct { 93 // the mapped port number, or 0 if unspecified 94 num int 95 // the mapped port name 96 name string 97 // a bool indicating if the mapped port name was explicitly set on the TargetPort field, or inferred from k8s' port.Name 98 explicitName bool 99 } 100 101 func findServiceTargetPort(servicePort *model.Port, k8sService *v1.Service) serviceTargetPort { 102 for _, p := range k8sService.Spec.Ports { 103 // TODO(@hzxuzhonghu): check protocol as well as port 104 if p.Name == servicePort.Name || p.Port == int32(servicePort.Port) { 105 if p.TargetPort.Type == intstr.Int && p.TargetPort.IntVal > 0 { 106 return serviceTargetPort{num: int(p.TargetPort.IntVal), name: p.Name, explicitName: false} 107 } 108 return serviceTargetPort{num: 0, name: p.TargetPort.StrVal, explicitName: true} 109 } 110 } 111 // should never happen 112 log.Debugf("did not find matching target port for %v on service %s", servicePort, k8sService.Name) 113 return serviceTargetPort{num: 0, name: "", explicitName: false} 114 } 115 116 func getPodServices(allServices []*v1.Service, pod *v1.Pod) []*v1.Service { 117 var services []*v1.Service 118 for _, service := range allServices { 119 if labels.Instance(service.Spec.Selector).Match(pod.Labels) { 120 services = append(services, service) 121 } 122 } 123 124 return services 125 } 126 127 func getNodeSelectorsForService(svc *v1.Service) labels.Instance { 128 if nodeSelector := svc.Annotations[annotation.TrafficNodeSelector.Name]; nodeSelector != "" { 129 var nodeSelectorKV map[string]string 130 if err := json.Unmarshal([]byte(nodeSelector), &nodeSelectorKV); err != nil { 131 log.Debugf("failed to unmarshal node selector annotation value for service %s.%s: %v", 132 svc.Name, svc.Namespace, err) 133 } 134 return nodeSelectorKV 135 } 136 return nil 137 } 138 139 func nodeEquals(a, b kubernetesNode) bool { 140 return a.address == b.address && a.labels.Equals(b.labels) 141 } 142 143 func isNodePortGatewayService(svc *v1.Service) bool { 144 if svc == nil { 145 return false 146 } 147 _, ok := svc.Annotations[annotation.TrafficNodeSelector.Name] 148 return ok && svc.Spec.Type == v1.ServiceTypeNodePort 149 } 150 151 // Get the pod key of the proxy which can be used to get pod from the informer cache 152 func podKeyByProxy(proxy *model.Proxy) types.NamespacedName { 153 parts := strings.Split(proxy.ID, ".") 154 if len(parts) == 2 && proxy.Metadata.Namespace == parts[1] { 155 return types.NamespacedName{Name: parts[0], Namespace: parts[1]} 156 } 157 158 return types.NamespacedName{} 159 } 160 161 func namespacedNameForService(svc *model.Service) types.NamespacedName { 162 return types.NamespacedName{ 163 Namespace: svc.Attributes.Namespace, 164 Name: svc.Attributes.Name, 165 } 166 } 167 168 // serviceClusterSetLocalHostname produces Kubernetes Multi-Cluster Services (MCS) ClusterSet FQDN for a k8s service 169 func serviceClusterSetLocalHostname(nn types.NamespacedName) host.Name { 170 return host.Name(nn.Name + "." + nn.Namespace + "." + "svc" + "." + constants.DefaultClusterSetLocalDomain) 171 } 172 173 // serviceClusterSetLocalHostnameForKR calls serviceClusterSetLocalHostname with the name and namespace of the given kubernetes resource. 174 func serviceClusterSetLocalHostnameForKR(obj metav1.Object) host.Name { 175 return serviceClusterSetLocalHostname(config.NamespacedName(obj)) 176 } 177 178 func labelRequirement(key string, op selection.Operator, vals []string, opts ...field.PathOption) *klabels.Requirement { 179 out, err := klabels.NewRequirement(key, op, vals, opts...) 180 if err != nil { 181 panic(fmt.Sprintf("failed creating requirements for Service: %v", err)) 182 } 183 return out 184 }