istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/util/workloadinstances/index.go (about)

     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  
    15  package workloadinstances
    16  
    17  import (
    18  	"sync"
    19  
    20  	"istio.io/istio/pilot/pkg/model"
    21  	"istio.io/istio/pkg/util/sets"
    22  )
    23  
    24  // Index reprensents an index over workload instances from workload entries.
    25  //
    26  // Indexes are thread-safe.
    27  type Index interface {
    28  	// Insert adds/updates given workload instance to the index.
    29  	//
    30  	// Returns previous value in the index, or nil otherwise.
    31  	Insert(*model.WorkloadInstance) *model.WorkloadInstance
    32  	// Delete removes given workload instance from the index.
    33  	//
    34  	// Returns value removed from the index, or nil otherwise.
    35  	Delete(*model.WorkloadInstance) *model.WorkloadInstance
    36  	// GetByIP returns a list of all workload instances associated with a
    37  	// given IP address. The list is ordered by namespace/name.
    38  	//
    39  	// There are several use cases where multiple workload instances might
    40  	// have the same IP address:
    41  	// 1) there are multiple Istio Proxies running on a single host, e.g.
    42  	//    in 'router' mode or even in 'sidecar' mode.
    43  	// 2) workload instances have the same IP but different networks
    44  	GetByIP(string) []*model.WorkloadInstance
    45  	// Empty returns whether the index is empty.
    46  	Empty() bool
    47  	// ForEach iterates over all workload instances in the index.
    48  	ForEach(func(*model.WorkloadInstance))
    49  }
    50  
    51  // indexKey returns index key for a given workload instance.
    52  func indexKey(wi *model.WorkloadInstance) string {
    53  	return wi.Namespace + "/" + wi.Name
    54  }
    55  
    56  // NewIndex returns a new Index instance.
    57  func NewIndex() Index {
    58  	return &index{
    59  		keyFunc:       indexKey,
    60  		keyToInstance: make(map[string]*model.WorkloadInstance),
    61  		ipToKeys:      make(MultiValueMap),
    62  	}
    63  }
    64  
    65  // index implements Index.
    66  type index struct {
    67  	mu sync.RWMutex
    68  	// key function
    69  	keyFunc func(*model.WorkloadInstance) string
    70  	// map of namespace/name -> workload instance
    71  	keyToInstance map[string]*model.WorkloadInstance
    72  	// map of ip -> set of namespace/name
    73  	ipToKeys MultiValueMap
    74  }
    75  
    76  // Insert implements Index.
    77  func (i *index) Insert(wi *model.WorkloadInstance) *model.WorkloadInstance {
    78  	i.mu.Lock()
    79  	defer i.mu.Unlock()
    80  
    81  	key := i.keyFunc(wi)
    82  	// Check to see if the workload entry changed. If it did, clear the old entry
    83  	previous := i.keyToInstance[key]
    84  	if previous != nil && previous.Endpoint.Address != wi.Endpoint.Address {
    85  		i.ipToKeys.Delete(previous.Endpoint.Address, key)
    86  	}
    87  	i.keyToInstance[key] = wi
    88  	if wi.Endpoint.Address != "" {
    89  		i.ipToKeys.Insert(wi.Endpoint.Address, key)
    90  	}
    91  	return previous
    92  }
    93  
    94  // Delete implements Index.
    95  func (i *index) Delete(wi *model.WorkloadInstance) *model.WorkloadInstance {
    96  	i.mu.Lock()
    97  	defer i.mu.Unlock()
    98  
    99  	key := i.keyFunc(wi)
   100  	previous := i.keyToInstance[key]
   101  	if previous != nil {
   102  		i.ipToKeys.Delete(previous.Endpoint.Address, key)
   103  	}
   104  	i.ipToKeys.Delete(wi.Endpoint.Address, key)
   105  	delete(i.keyToInstance, key)
   106  	return previous
   107  }
   108  
   109  // GetByIP implements Index.
   110  func (i *index) GetByIP(ip string) []*model.WorkloadInstance {
   111  	i.mu.RLock()
   112  	defer i.mu.RUnlock()
   113  
   114  	keys := i.ipToKeys[ip]
   115  	if len(keys) == 0 {
   116  		return nil
   117  	}
   118  	instances := make([]*model.WorkloadInstance, 0, len(keys))
   119  	for _, key := range sets.SortedList(keys) {
   120  		if instance, exists := i.keyToInstance[key]; exists {
   121  			instances = append(instances, instance)
   122  		}
   123  	}
   124  	return instances
   125  }
   126  
   127  // Empty implements Index.
   128  func (i *index) Empty() bool {
   129  	i.mu.RLock()
   130  	defer i.mu.RUnlock()
   131  
   132  	return len(i.keyToInstance) == 0
   133  }
   134  
   135  // ForEach iterates over all workload instances in the index.
   136  func (i *index) ForEach(fn func(*model.WorkloadInstance)) {
   137  	i.mu.RLock()
   138  	defer i.mu.RUnlock()
   139  
   140  	for _, instance := range i.keyToInstance {
   141  		fn(instance)
   142  	}
   143  }