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