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  }