...

Source file src/k8s.io/kubernetes/pkg/controller/endpointslicemirroring/utils.go

Documentation: k8s.io/kubernetes/pkg/controller/endpointslicemirroring

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     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 endpointslicemirroring
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	corev1 "k8s.io/api/core/v1"
    24  	discovery "k8s.io/api/discovery/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    28  	"k8s.io/client-go/tools/cache"
    29  	"k8s.io/client-go/tools/leaderelection/resourcelock"
    30  	endpointsliceutil "k8s.io/endpointslice/util"
    31  	"k8s.io/kubernetes/pkg/apis/discovery/validation"
    32  	netutils "k8s.io/utils/net"
    33  )
    34  
    35  // addrTypePortMapKey is used to uniquely identify groups of endpoint ports and
    36  // address types.
    37  type addrTypePortMapKey string
    38  
    39  // newAddrTypePortMapKey generates a PortMapKey from endpoint ports.
    40  func newAddrTypePortMapKey(endpointPorts []discovery.EndpointPort, addrType discovery.AddressType) addrTypePortMapKey {
    41  	pmk := fmt.Sprintf("%s-%s", addrType, endpointsliceutil.NewPortMapKey(endpointPorts))
    42  	return addrTypePortMapKey(pmk)
    43  }
    44  
    45  func (pk addrTypePortMapKey) addressType() discovery.AddressType {
    46  	if strings.HasPrefix(string(pk), string(discovery.AddressTypeIPv6)) {
    47  		return discovery.AddressTypeIPv6
    48  	}
    49  	return discovery.AddressTypeIPv4
    50  }
    51  
    52  func getAddressType(address string) *discovery.AddressType {
    53  	ip := netutils.ParseIPSloppy(address)
    54  	if ip == nil {
    55  		return nil
    56  	}
    57  	addressType := discovery.AddressTypeIPv4
    58  	if ip.To4() == nil {
    59  		addressType = discovery.AddressTypeIPv6
    60  	}
    61  	return &addressType
    62  }
    63  
    64  // newEndpointSlice returns an EndpointSlice generated from an Endpoints
    65  // resource, ports, and address type.
    66  func newEndpointSlice(endpoints *corev1.Endpoints, ports []discovery.EndpointPort, addrType discovery.AddressType, sliceName string) *discovery.EndpointSlice {
    67  	gvk := schema.GroupVersionKind{Version: "v1", Kind: "Endpoints"}
    68  	ownerRef := metav1.NewControllerRef(endpoints, gvk)
    69  	epSlice := &discovery.EndpointSlice{
    70  		ObjectMeta: metav1.ObjectMeta{
    71  			Labels:          map[string]string{},
    72  			Annotations:     map[string]string{},
    73  			OwnerReferences: []metav1.OwnerReference{*ownerRef},
    74  			Namespace:       endpoints.Namespace,
    75  		},
    76  		Ports:       ports,
    77  		AddressType: addrType,
    78  		Endpoints:   []discovery.Endpoint{},
    79  	}
    80  
    81  	// clone all labels
    82  	for label, val := range endpoints.Labels {
    83  		epSlice.Labels[label] = val
    84  	}
    85  
    86  	// overwrite specific labels
    87  	epSlice.Labels[discovery.LabelServiceName] = endpoints.Name
    88  	epSlice.Labels[discovery.LabelManagedBy] = controllerName
    89  
    90  	// clone all annotations but EndpointsLastChangeTriggerTime and LastAppliedConfigAnnotation
    91  	for annotation, val := range endpoints.Annotations {
    92  		if annotation == corev1.EndpointsLastChangeTriggerTime || annotation == corev1.LastAppliedConfigAnnotation {
    93  			continue
    94  		}
    95  		epSlice.Annotations[annotation] = val
    96  	}
    97  
    98  	if sliceName == "" {
    99  		epSlice.GenerateName = getEndpointSlicePrefix(endpoints.Name)
   100  	} else {
   101  		epSlice.Name = sliceName
   102  	}
   103  
   104  	return epSlice
   105  }
   106  
   107  // getEndpointSlicePrefix returns a suitable prefix for an EndpointSlice name.
   108  func getEndpointSlicePrefix(serviceName string) string {
   109  	// use the dash (if the name isn't too long) to make the name a bit prettier.
   110  	prefix := fmt.Sprintf("%s-", serviceName)
   111  	if len(validation.ValidateEndpointSliceName(prefix, true)) != 0 {
   112  		prefix = serviceName
   113  	}
   114  	return prefix
   115  }
   116  
   117  // addressToEndpoint converts an address from an Endpoints resource to an
   118  // EndpointSlice endpoint.
   119  func addressToEndpoint(address corev1.EndpointAddress, ready bool) *discovery.Endpoint {
   120  	endpoint := &discovery.Endpoint{
   121  		Addresses: []string{address.IP},
   122  		Conditions: discovery.EndpointConditions{
   123  			Ready: &ready,
   124  		},
   125  		TargetRef: address.TargetRef,
   126  	}
   127  
   128  	if address.NodeName != nil {
   129  		endpoint.NodeName = address.NodeName
   130  	}
   131  	if address.Hostname != "" {
   132  		endpoint.Hostname = &address.Hostname
   133  	}
   134  
   135  	return endpoint
   136  }
   137  
   138  // epPortsToEpsPorts converts ports from an Endpoints resource to ports for an
   139  // EndpointSlice resource.
   140  func epPortsToEpsPorts(epPorts []corev1.EndpointPort) []discovery.EndpointPort {
   141  	epsPorts := []discovery.EndpointPort{}
   142  	for _, epPort := range epPorts {
   143  		epp := epPort.DeepCopy()
   144  		epsPorts = append(epsPorts, discovery.EndpointPort{
   145  			Name:        &epp.Name,
   146  			Port:        &epp.Port,
   147  			Protocol:    &epp.Protocol,
   148  			AppProtocol: epp.AppProtocol,
   149  		})
   150  	}
   151  	return epsPorts
   152  }
   153  
   154  // getServiceFromDeleteAction parses a Service resource from a delete
   155  // action.
   156  func getServiceFromDeleteAction(obj interface{}) *corev1.Service {
   157  	if service, ok := obj.(*corev1.Service); ok {
   158  		return service
   159  	}
   160  	// If we reached here it means the Service was deleted but its final state
   161  	// is unrecorded.
   162  	tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
   163  	if !ok {
   164  		utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
   165  		return nil
   166  	}
   167  	service, ok := tombstone.Obj.(*corev1.Service)
   168  	if !ok {
   169  		utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a Service resource: %#v", obj))
   170  		return nil
   171  	}
   172  	return service
   173  }
   174  
   175  // getEndpointsFromDeleteAction parses an Endpoints resource from a delete
   176  // action.
   177  func getEndpointsFromDeleteAction(obj interface{}) *corev1.Endpoints {
   178  	if endpoints, ok := obj.(*corev1.Endpoints); ok {
   179  		return endpoints
   180  	}
   181  	// If we reached here it means the Endpoints resource was deleted but its
   182  	// final state is unrecorded.
   183  	tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
   184  	if !ok {
   185  		utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
   186  		return nil
   187  	}
   188  	endpoints, ok := tombstone.Obj.(*corev1.Endpoints)
   189  	if !ok {
   190  		utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not an Endpoints resource: %#v", obj))
   191  		return nil
   192  	}
   193  	return endpoints
   194  }
   195  
   196  // getEndpointSliceFromDeleteAction parses an EndpointSlice from a delete action.
   197  func getEndpointSliceFromDeleteAction(obj interface{}) *discovery.EndpointSlice {
   198  	if endpointSlice, ok := obj.(*discovery.EndpointSlice); ok {
   199  		return endpointSlice
   200  	}
   201  	// If we reached here it means the EndpointSlice was deleted but its final
   202  	// state is unrecorded.
   203  	tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
   204  	if !ok {
   205  		utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
   206  		return nil
   207  	}
   208  	endpointSlice, ok := tombstone.Obj.(*discovery.EndpointSlice)
   209  	if !ok {
   210  		utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not an EndpointSlice resource: %#v", obj))
   211  		return nil
   212  	}
   213  	return endpointSlice
   214  }
   215  
   216  // endpointsControllerKey returns a controller key for an Endpoints resource but
   217  // derived from an EndpointSlice.
   218  func endpointsControllerKey(endpointSlice *discovery.EndpointSlice) (string, error) {
   219  	if endpointSlice == nil {
   220  		return "", fmt.Errorf("nil EndpointSlice passed to serviceControllerKey()")
   221  	}
   222  	serviceName, ok := endpointSlice.Labels[discovery.LabelServiceName]
   223  	if !ok || serviceName == "" {
   224  		return "", fmt.Errorf("EndpointSlice missing %s label", discovery.LabelServiceName)
   225  	}
   226  	return fmt.Sprintf("%s/%s", endpointSlice.Namespace, serviceName), nil
   227  }
   228  
   229  // skipMirror return true if the LabelSkipMirror label has been set to
   230  // "true".
   231  func skipMirror(labels map[string]string) bool {
   232  	skipMirror, _ := labels[discovery.LabelSkipMirror]
   233  	return skipMirror == "true"
   234  }
   235  
   236  // hasLeaderElection returns true if the LeaderElectionRecordAnnotationKey is
   237  // set as an annotation.
   238  func hasLeaderElection(annotations map[string]string) bool {
   239  	_, ok := annotations[resourcelock.LeaderElectionRecordAnnotationKey]
   240  	return ok
   241  }
   242  
   243  // cloneAndRemoveKeys is a copy of CloneAndRemoveLabels
   244  // it is used here for annotations and labels
   245  func cloneAndRemoveKeys(a map[string]string, keys ...string) map[string]string {
   246  	if len(keys) == 0 {
   247  		// Don't need to remove a key.
   248  		return a
   249  	}
   250  	// Clone.
   251  	newMap := map[string]string{}
   252  	for k, v := range a {
   253  		newMap[k] = v
   254  	}
   255  	// remove keys
   256  	for _, key := range keys {
   257  		delete(newMap, key)
   258  	}
   259  	return newMap
   260  }
   261  
   262  // managedByChanged returns true if one of the provided EndpointSlices is
   263  // managed by the EndpointSlice controller while the other is not.
   264  func managedByChanged(endpointSlice1, endpointSlice2 *discovery.EndpointSlice) bool {
   265  	return managedByController(endpointSlice1) != managedByController(endpointSlice2)
   266  }
   267  
   268  // managedByController returns true if the controller of the provided
   269  // EndpointSlices is the EndpointSlice controller.
   270  func managedByController(endpointSlice *discovery.EndpointSlice) bool {
   271  	managedBy, _ := endpointSlice.Labels[discovery.LabelManagedBy]
   272  	return managedBy == controllerName
   273  }
   274  

View as plain text