github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/utils.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package k8s 18 19 import ( 20 "fmt" 21 "reflect" 22 "strings" 23 24 "github.com/golang/glog" 25 v1 "k8s.io/api/core/v1" 26 networking "k8s.io/api/networking/v1beta1" 27 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apimachinery/pkg/util/intstr" 30 "k8s.io/apimachinery/pkg/util/version" 31 "k8s.io/client-go/kubernetes" 32 "k8s.io/client-go/tools/cache" 33 ) 34 35 // storeToIngressLister makes a Store that lists Ingress. 36 // TODO: Move this to cache/listers post 1.1. 37 type storeToIngressLister struct { 38 cache.Store 39 } 40 41 // GetByKeySafe calls Store.GetByKeySafe and returns a copy of the ingress so it is 42 // safe to modify. 43 func (s *storeToIngressLister) GetByKeySafe(key string) (ing *networking.Ingress, exists bool, err error) { 44 item, exists, err := s.Store.GetByKey(key) 45 if !exists || err != nil { 46 return nil, exists, err 47 } 48 ing = item.(*networking.Ingress).DeepCopy() 49 return 50 } 51 52 // List lists all Ingress' in the store. 53 func (s *storeToIngressLister) List() (ing networking.IngressList, err error) { 54 for _, m := range s.Store.List() { 55 ing.Items = append(ing.Items, *(m.(*networking.Ingress)).DeepCopy()) 56 } 57 return ing, nil 58 } 59 60 // GetServiceIngress gets all the Ingress' that have rules pointing to a service. 61 // Note that this ignores services without the right nodePorts. 62 func (s *storeToIngressLister) GetServiceIngress(svc *v1.Service) (ings []networking.Ingress, err error) { 63 for _, m := range s.Store.List() { 64 ing := *m.(*networking.Ingress).DeepCopy() 65 if ing.Namespace != svc.Namespace { 66 continue 67 } 68 if ing.Spec.Backend != nil { 69 if ing.Spec.Backend.ServiceName == svc.Name { 70 ings = append(ings, ing) 71 } 72 } 73 for _, rules := range ing.Spec.Rules { 74 if rules.IngressRuleValue.HTTP == nil { 75 continue 76 } 77 for _, p := range rules.IngressRuleValue.HTTP.Paths { 78 if p.Backend.ServiceName == svc.Name { 79 ings = append(ings, ing) 80 } 81 } 82 } 83 } 84 if len(ings) == 0 { 85 err = fmt.Errorf("No ingress for service %v", svc.Name) 86 } 87 return 88 } 89 90 // storeToConfigMapLister makes a Store that lists ConfigMaps 91 type storeToConfigMapLister struct { 92 cache.Store 93 } 94 95 // List lists all Ingress' in the store. 96 func (s *storeToConfigMapLister) List() (cfgm v1.ConfigMapList, err error) { 97 for _, m := range s.Store.List() { 98 cfgm.Items = append(cfgm.Items, *(m.(*v1.ConfigMap))) 99 } 100 return cfgm, nil 101 } 102 103 // indexerToPodLister makes a Indexer that lists Pods. 104 type indexerToPodLister struct { 105 cache.Indexer 106 } 107 108 // ListByNamespace lists all Pods in the indexer for a given namespace that match the provided selector. 109 func (ipl indexerToPodLister) ListByNamespace(ns string, selector labels.Selector) (pods []*v1.Pod, err error) { 110 err = cache.ListAllByNamespace(ipl.Indexer, ns, selector, func(m interface{}) { 111 pods = append(pods, m.(*v1.Pod)) 112 }) 113 return pods, err 114 } 115 116 // storeToEndpointLister makes a Store that lists Endponts 117 type storeToEndpointLister struct { 118 cache.Store 119 } 120 121 // GetServiceEndpoints returns the endpoints of a service, matched on service name. 122 func (s *storeToEndpointLister) GetServiceEndpoints(svc *v1.Service) (ep v1.Endpoints, err error) { 123 for _, m := range s.Store.List() { 124 ep = *m.(*v1.Endpoints) 125 if svc.Name == ep.Name && svc.Namespace == ep.Namespace { 126 return ep, nil 127 } 128 } 129 return ep, fmt.Errorf("could not find endpoints for service: %v", svc.Name) 130 } 131 132 // findPort locates the container port for the given pod and portName. If the 133 // targetPort is a number, use that. If the targetPort is a string, look that 134 // string up in all named ports in all containers in the target pod. If no 135 // match is found, fail. 136 func findPort(pod *v1.Pod, svcPort v1.ServicePort) (int32, error) { 137 portName := svcPort.TargetPort 138 switch portName.Type { 139 case intstr.String: 140 name := portName.StrVal 141 for _, container := range pod.Spec.Containers { 142 for _, port := range container.Ports { 143 if port.Name == name && port.Protocol == svcPort.Protocol { 144 return port.ContainerPort, nil 145 } 146 } 147 } 148 case intstr.Int: 149 return int32(portName.IntValue()), nil 150 } 151 152 return 0, fmt.Errorf("no suitable port for manifest: %s", pod.UID) 153 } 154 155 // isMinion determines is an ingress is a minion or not 156 func isMinion(ing *networking.Ingress) bool { 157 return ing.Annotations["nginx.org/mergeable-ingress-type"] == "minion" 158 } 159 160 // isMaster determines is an ingress is a master or not 161 func isMaster(ing *networking.Ingress) bool { 162 return ing.Annotations["nginx.org/mergeable-ingress-type"] == "master" 163 } 164 165 // hasChanges determines if current ingress has changes compared to old ingress 166 func hasChanges(old *networking.Ingress, current *networking.Ingress) bool { 167 old.Status.LoadBalancer.Ingress = current.Status.LoadBalancer.Ingress 168 old.ResourceVersion = current.ResourceVersion 169 return !reflect.DeepEqual(old, current) 170 } 171 172 // ParseNamespaceName parses the string in the <namespace>/<name> format and returns the name and the namespace. 173 // It returns an error in case the string does not follow the <namespace>/<name> format. 174 func ParseNamespaceName(value string) (ns string, name string, err error) { 175 res := strings.Split(value, "/") 176 if len(res) != 2 { 177 return "", "", fmt.Errorf("%q must follow the format <namespace>/<name>", value) 178 } 179 return res[0], res[1], nil 180 } 181 182 // GetK8sVersion returns the running version of k8s 183 func GetK8sVersion(client kubernetes.Interface) (v *version.Version, err error) { 184 serverVersion, err := client.Discovery().ServerVersion() 185 if err != nil { 186 return nil, err 187 } 188 189 runningVersion, err := version.ParseGeneric(serverVersion.String()) 190 if err != nil { 191 return nil, fmt.Errorf("unexpected error parsing running Kubernetes version: %w", err) 192 } 193 glog.V(3).Infof("Kubernetes version: %v", runningVersion) 194 195 return runningVersion, nil 196 }