github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/api/endpoints/util.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 endpoints
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/md5"
    22  	"encoding/hex"
    23  	"hash"
    24  	"sort"
    25  
    26  	"k8s.io/kubernetes/pkg/api"
    27  	"k8s.io/kubernetes/pkg/types"
    28  	"k8s.io/kubernetes/pkg/util"
    29  )
    30  
    31  // RepackSubsets takes a slice of EndpointSubset objects, expands it to the full
    32  // representation, and then repacks that into the canonical layout.  This
    33  // ensures that code which operates on these objects can rely on the common
    34  // form for things like comparison.  The result is a newly allocated slice.
    35  func RepackSubsets(subsets []api.EndpointSubset) []api.EndpointSubset {
    36  	// First map each unique port definition to the sets of hosts that
    37  	// offer it.
    38  	allAddrs := map[addressKey]*api.EndpointAddress{}
    39  	portToAddrReadyMap := map[api.EndpointPort]addressSet{}
    40  	for i := range subsets {
    41  		for _, port := range subsets[i].Ports {
    42  			for k := range subsets[i].Addresses {
    43  				mapAddressByPort(&subsets[i].Addresses[k], port, true, allAddrs, portToAddrReadyMap)
    44  			}
    45  			for k := range subsets[i].NotReadyAddresses {
    46  				mapAddressByPort(&subsets[i].NotReadyAddresses[k], port, false, allAddrs, portToAddrReadyMap)
    47  			}
    48  		}
    49  	}
    50  
    51  	// Next, map the sets of hosts to the sets of ports they offer.
    52  	// Go does not allow maps or slices as keys to maps, so we have
    53  	// to synthesize an artificial key and do a sort of 2-part
    54  	// associative entity.
    55  	type keyString string
    56  	keyToAddrReadyMap := map[keyString]addressSet{}
    57  	addrReadyMapKeyToPorts := map[keyString][]api.EndpointPort{}
    58  	for port, addrs := range portToAddrReadyMap {
    59  		key := keyString(hashAddresses(addrs))
    60  		keyToAddrReadyMap[key] = addrs
    61  		addrReadyMapKeyToPorts[key] = append(addrReadyMapKeyToPorts[key], port)
    62  	}
    63  
    64  	// Next, build the N-to-M association the API wants.
    65  	final := []api.EndpointSubset{}
    66  	for key, ports := range addrReadyMapKeyToPorts {
    67  		var readyAddrs, notReadyAddrs []api.EndpointAddress
    68  		for addr, ready := range keyToAddrReadyMap[key] {
    69  			if ready {
    70  				readyAddrs = append(readyAddrs, *addr)
    71  			} else {
    72  				notReadyAddrs = append(notReadyAddrs, *addr)
    73  			}
    74  		}
    75  		final = append(final, api.EndpointSubset{Addresses: readyAddrs, NotReadyAddresses: notReadyAddrs, Ports: ports})
    76  	}
    77  
    78  	// Finally, sort it.
    79  	return SortSubsets(final)
    80  }
    81  
    82  // The sets of hosts must be de-duped, using IP+UID as the key.
    83  type addressKey struct {
    84  	ip  string
    85  	uid types.UID
    86  }
    87  
    88  // mapAddressByPort adds an address into a map by its ports, registering the address with a unique pointer, and preserving
    89  // any existing ready state.
    90  func mapAddressByPort(addr *api.EndpointAddress, port api.EndpointPort, ready bool, allAddrs map[addressKey]*api.EndpointAddress, portToAddrReadyMap map[api.EndpointPort]addressSet) *api.EndpointAddress {
    91  	// use addressKey to distinguish between two endpoints that are identical addresses
    92  	// but may have come from different hosts, for attribution. For instance, Mesos
    93  	// assigns pods the node IP, but the pods are distinct.
    94  	key := addressKey{ip: addr.IP}
    95  	if addr.TargetRef != nil {
    96  		key.uid = addr.TargetRef.UID
    97  	}
    98  
    99  	// Accumulate the address. The full EndpointAddress structure is preserved for use when
   100  	// we rebuild the subsets so that the final TargetRef has all of the necessary data.
   101  	existingAddress := allAddrs[key]
   102  	if existingAddress == nil {
   103  		// Make a copy so we don't write to the
   104  		// input args of this function.
   105  		existingAddress = &api.EndpointAddress{}
   106  		*existingAddress = *addr
   107  		allAddrs[key] = existingAddress
   108  	}
   109  
   110  	// Remember that this port maps to this address.
   111  	if _, found := portToAddrReadyMap[port]; !found {
   112  		portToAddrReadyMap[port] = addressSet{}
   113  	}
   114  	// if we have not yet recorded this port for this address, or if the previous
   115  	// state was ready, write the current ready state. not ready always trumps
   116  	// ready.
   117  	if wasReady, found := portToAddrReadyMap[port][existingAddress]; !found || wasReady {
   118  		portToAddrReadyMap[port][existingAddress] = ready
   119  	}
   120  	return existingAddress
   121  }
   122  
   123  type addressSet map[*api.EndpointAddress]bool
   124  
   125  type addrReady struct {
   126  	addr  *api.EndpointAddress
   127  	ready bool
   128  }
   129  
   130  func hashAddresses(addrs addressSet) string {
   131  	// Flatten the list of addresses into a string so it can be used as a
   132  	// map key.  Unfortunately, DeepHashObject is implemented in terms of
   133  	// spew, and spew does not handle non-primitive map keys well.  So
   134  	// first we collapse it into a slice, sort the slice, then hash that.
   135  	slice := make([]addrReady, 0, len(addrs))
   136  	for k, ready := range addrs {
   137  		slice = append(slice, addrReady{k, ready})
   138  	}
   139  	sort.Sort(addrsReady(slice))
   140  	hasher := md5.New()
   141  	util.DeepHashObject(hasher, slice)
   142  	return hex.EncodeToString(hasher.Sum(nil)[0:])
   143  }
   144  
   145  func lessAddrReady(a, b addrReady) bool {
   146  	// ready is not significant to hashing since we can't have duplicate addresses
   147  	return LessEndpointAddress(a.addr, b.addr)
   148  }
   149  
   150  type addrsReady []addrReady
   151  
   152  func (sl addrsReady) Len() int      { return len(sl) }
   153  func (sl addrsReady) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
   154  func (sl addrsReady) Less(i, j int) bool {
   155  	return lessAddrReady(sl[i], sl[j])
   156  }
   157  
   158  func LessEndpointAddress(a, b *api.EndpointAddress) bool {
   159  	ipComparison := bytes.Compare([]byte(a.IP), []byte(b.IP))
   160  	if ipComparison != 0 {
   161  		return ipComparison < 0
   162  	}
   163  	if b.TargetRef == nil {
   164  		return false
   165  	}
   166  	if a.TargetRef == nil {
   167  		return true
   168  	}
   169  	return a.TargetRef.UID < b.TargetRef.UID
   170  }
   171  
   172  type addrPtrsByIpAndUID []*api.EndpointAddress
   173  
   174  func (sl addrPtrsByIpAndUID) Len() int      { return len(sl) }
   175  func (sl addrPtrsByIpAndUID) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
   176  func (sl addrPtrsByIpAndUID) Less(i, j int) bool {
   177  	return LessEndpointAddress(sl[i], sl[j])
   178  }
   179  
   180  // SortSubsets sorts an array of EndpointSubset objects in place.  For ease of
   181  // use it returns the input slice.
   182  func SortSubsets(subsets []api.EndpointSubset) []api.EndpointSubset {
   183  	for i := range subsets {
   184  		ss := &subsets[i]
   185  		sort.Sort(addrsByIpAndUID(ss.Addresses))
   186  		sort.Sort(addrsByIpAndUID(ss.NotReadyAddresses))
   187  		sort.Sort(portsByHash(ss.Ports))
   188  	}
   189  	sort.Sort(subsetsByHash(subsets))
   190  	return subsets
   191  }
   192  
   193  func hashObject(hasher hash.Hash, obj interface{}) []byte {
   194  	util.DeepHashObject(hasher, obj)
   195  	return hasher.Sum(nil)
   196  }
   197  
   198  type subsetsByHash []api.EndpointSubset
   199  
   200  func (sl subsetsByHash) Len() int      { return len(sl) }
   201  func (sl subsetsByHash) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
   202  func (sl subsetsByHash) Less(i, j int) bool {
   203  	hasher := md5.New()
   204  	h1 := hashObject(hasher, sl[i])
   205  	h2 := hashObject(hasher, sl[j])
   206  	return bytes.Compare(h1, h2) < 0
   207  }
   208  
   209  type addrsByIpAndUID []api.EndpointAddress
   210  
   211  func (sl addrsByIpAndUID) Len() int      { return len(sl) }
   212  func (sl addrsByIpAndUID) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
   213  func (sl addrsByIpAndUID) Less(i, j int) bool {
   214  	return LessEndpointAddress(&sl[i], &sl[j])
   215  }
   216  
   217  type portsByHash []api.EndpointPort
   218  
   219  func (sl portsByHash) Len() int      { return len(sl) }
   220  func (sl portsByHash) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
   221  func (sl portsByHash) Less(i, j int) bool {
   222  	hasher := md5.New()
   223  	h1 := hashObject(hasher, sl[i])
   224  	h2 := hashObject(hasher, sl[j])
   225  	return bytes.Compare(h1, h2) < 0
   226  }