github.phpd.cn/cilium/cilium@v1.6.12/pkg/identity/cache/cache.go (about)

     1  // Copyright 2016-2018 Authors of Cilium
     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 cache
    16  
    17  import (
    18  	"context"
    19  	"reflect"
    20  
    21  	"github.com/cilium/cilium/api/v1/models"
    22  	"github.com/cilium/cilium/pkg/allocator"
    23  	"github.com/cilium/cilium/pkg/identity"
    24  	"github.com/cilium/cilium/pkg/idpool"
    25  	"github.com/cilium/cilium/pkg/kvstore"
    26  	"github.com/cilium/cilium/pkg/labels"
    27  	"github.com/cilium/cilium/pkg/logging"
    28  	"github.com/cilium/cilium/pkg/logging/logfields"
    29  )
    30  
    31  var (
    32  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "identity-cache")
    33  )
    34  
    35  // IdentityCache is a cache of identity to labels mapping
    36  type IdentityCache map[identity.NumericIdentity]labels.LabelArray
    37  
    38  // IdentitiesModel is a wrapper so that we can implement the sort.Interface
    39  // to sort the slice by ID
    40  type IdentitiesModel []*models.Identity
    41  
    42  // Less returns true if the element in index `i` is lower than the element
    43  // in index `j`
    44  func (s IdentitiesModel) Less(i, j int) bool {
    45  	return s[i].ID < s[j].ID
    46  }
    47  
    48  // GetIdentityCache returns a cache of all known identities
    49  func GetIdentityCache() IdentityCache {
    50  	cache := IdentityCache{}
    51  
    52  	if IdentityAllocator != nil {
    53  
    54  		IdentityAllocator.ForeachCache(func(id idpool.ID, val allocator.AllocatorKey) {
    55  			if val != nil {
    56  				if gi, ok := val.(GlobalIdentity); ok {
    57  					cache[identity.NumericIdentity(id)] = gi.LabelArray
    58  				} else {
    59  					log.Warningf("Ignoring unknown identity type '%s': %+v",
    60  						reflect.TypeOf(val), val)
    61  				}
    62  			}
    63  		})
    64  	}
    65  
    66  	for key, identity := range identity.ReservedIdentityCache {
    67  		cache[key] = identity.Labels.LabelArray()
    68  	}
    69  
    70  	if localIdentities != nil {
    71  		for _, identity := range localIdentities.GetIdentities() {
    72  			cache[identity.ID] = identity.Labels.LabelArray()
    73  		}
    74  	}
    75  
    76  	return cache
    77  }
    78  
    79  // GetIdentities returns all known identities
    80  func GetIdentities() IdentitiesModel {
    81  	identities := IdentitiesModel{}
    82  
    83  	IdentityAllocator.ForeachCache(func(id idpool.ID, val allocator.AllocatorKey) {
    84  		if gi, ok := val.(GlobalIdentity); ok {
    85  			identity := identity.NewIdentityFromLabelArray(identity.NumericIdentity(id), gi.LabelArray)
    86  			identities = append(identities, identity.GetModel())
    87  		}
    88  
    89  	})
    90  	// append user reserved identities
    91  	for _, v := range identity.ReservedIdentityCache {
    92  		identities = append(identities, v.GetModel())
    93  	}
    94  
    95  	for _, v := range localIdentities.GetIdentities() {
    96  		identities = append(identities, v.GetModel())
    97  	}
    98  
    99  	return identities
   100  }
   101  
   102  type identityWatcher struct {
   103  	stopChan chan bool
   104  }
   105  
   106  // collectEvent records the 'event' as an added or deleted identity,
   107  // and makes sure that any identity is present in only one of the sets
   108  // (added or deleted).
   109  func collectEvent(event allocator.AllocatorEvent, added, deleted IdentityCache) bool {
   110  	id := identity.NumericIdentity(event.ID)
   111  	// Only create events have the key
   112  	if event.Typ == kvstore.EventTypeCreate {
   113  		if gi, ok := event.Key.(GlobalIdentity); ok {
   114  			// Un-delete the added ID if previously
   115  			// 'deleted' so that collected events can be
   116  			// processed in any order.
   117  			if _, exists := deleted[id]; exists {
   118  				delete(deleted, id)
   119  			}
   120  			added[id] = gi.LabelArray
   121  			return true
   122  		}
   123  		log.Warningf("collectEvent: Ignoring unknown identity type '%s': %+v",
   124  			reflect.TypeOf(event.Key), event.Key)
   125  		return false
   126  	}
   127  	// Reverse an add when subsequently deleted
   128  	if _, exists := added[id]; exists {
   129  		delete(added, id)
   130  	}
   131  	// record the id deleted even if an add was reversed, as the
   132  	// id may also have previously existed, in which case the
   133  	// result is not no-op!
   134  	deleted[id] = labels.LabelArray{}
   135  
   136  	return true
   137  }
   138  
   139  // watch starts the identity watcher
   140  func (w *identityWatcher) watch(owner IdentityAllocatorOwner, events allocator.AllocatorEventChan) {
   141  	w.stopChan = make(chan bool)
   142  
   143  	go func() {
   144  		for {
   145  			added := IdentityCache{}
   146  			deleted := IdentityCache{}
   147  
   148  		First:
   149  			for {
   150  				// Wait for one identity add or delete or stop
   151  				select {
   152  				case event, ok := <-events:
   153  					if !ok {
   154  						// 'events' was closed
   155  						return
   156  					}
   157  					// Collect first added and deleted labels
   158  					switch event.Typ {
   159  					case kvstore.EventTypeCreate, kvstore.EventTypeDelete:
   160  						if collectEvent(event, added, deleted) {
   161  							// First event collected
   162  							break First
   163  						}
   164  					default:
   165  						// Ignore modify events
   166  					}
   167  				case <-w.stopChan:
   168  					return
   169  				}
   170  			}
   171  
   172  		More:
   173  			for {
   174  				// see if there is more, but do not wait nor stop
   175  				select {
   176  				case event, ok := <-events:
   177  					if !ok {
   178  						// 'events' was closed
   179  						break More
   180  					}
   181  					// Collect more added and deleted labels
   182  					switch event.Typ {
   183  					case kvstore.EventTypeCreate, kvstore.EventTypeDelete:
   184  						collectEvent(event, added, deleted)
   185  					default:
   186  						// Ignore modify events
   187  					}
   188  				default:
   189  					// No more events available without blocking
   190  					break More
   191  				}
   192  			}
   193  			// Issue collected updates
   194  			owner.UpdateIdentities(added, deleted) // disjoint sets
   195  		}
   196  	}()
   197  }
   198  
   199  // stop stops the identity watcher
   200  func (w *identityWatcher) stop() {
   201  	close(w.stopChan)
   202  }
   203  
   204  // LookupIdentity looks up the identity by its labels but does not create it.
   205  // This function will first search through the local cache and fall back to
   206  // querying the kvstore.
   207  func LookupIdentity(lbls labels.Labels) *identity.Identity {
   208  	if reservedIdentity := LookupReservedIdentityByLabels(lbls); reservedIdentity != nil {
   209  		return reservedIdentity
   210  	}
   211  
   212  	if identity := localIdentities.lookup(lbls); identity != nil {
   213  		return identity
   214  	}
   215  
   216  	if IdentityAllocator == nil {
   217  		return nil
   218  	}
   219  
   220  	lblArray := lbls.LabelArray()
   221  	id, err := IdentityAllocator.Get(context.TODO(), GlobalIdentity{lblArray})
   222  	if err != nil {
   223  		return nil
   224  	}
   225  
   226  	if id == idpool.NoID {
   227  		return nil
   228  	}
   229  
   230  	return identity.NewIdentityFromLabelArray(identity.NumericIdentity(id), lblArray)
   231  }
   232  
   233  // LookupReservedIdentityByLabels looks up a reserved identity by its labels and
   234  // returns it if found. Returns nil if not found.
   235  func LookupReservedIdentityByLabels(lbls labels.Labels) *identity.Identity {
   236  	if identity := identity.WellKnown.LookupByLabels(lbls); identity != nil {
   237  		return identity
   238  	}
   239  
   240  	for _, lbl := range lbls {
   241  		switch {
   242  		// If the set of labels contain a fixed identity then and exists in
   243  		// the map of reserved IDs then return the identity of that reserved ID.
   244  		case lbl.Key == labels.LabelKeyFixedIdentity:
   245  			id := identity.GetReservedID(lbl.Value)
   246  			if id != identity.IdentityUnknown && identity.IsUserReservedIdentity(id) {
   247  				return identity.LookupReservedIdentity(id)
   248  			}
   249  			// If a fixed identity was not found then we return nil to avoid
   250  			// falling to a reserved identity.
   251  			return nil
   252  		// If it doesn't contain a fixed-identity then make sure the set of
   253  		// labels only contains a single label and that label is of the reserved
   254  		// type. This is to prevent users from adding cilium-reserved labels
   255  		// into the workloads.
   256  		case lbl.Source == labels.LabelSourceReserved:
   257  			if len(lbls) != 1 {
   258  				return nil
   259  			}
   260  			id := identity.GetReservedID(lbl.Key)
   261  			if id != identity.IdentityUnknown && !identity.IsUserReservedIdentity(id) {
   262  				return identity.LookupReservedIdentity(id)
   263  			}
   264  		}
   265  	}
   266  	return nil
   267  }
   268  
   269  var unknownIdentity = identity.NewIdentity(identity.IdentityUnknown, labels.Labels{labels.IDNameUnknown: labels.NewLabel(labels.IDNameUnknown, "", labels.LabelSourceReserved)})
   270  
   271  // LookupIdentityByID returns the identity by ID. This function will first
   272  // search through the local cache and fall back to querying the kvstore.
   273  func LookupIdentityByID(id identity.NumericIdentity) *identity.Identity {
   274  	if id == identity.IdentityUnknown {
   275  		return unknownIdentity
   276  	}
   277  
   278  	if identity := identity.LookupReservedIdentity(id); identity != nil {
   279  		return identity
   280  	}
   281  
   282  	if IdentityAllocator == nil {
   283  		return nil
   284  	}
   285  
   286  	if identity := localIdentities.lookupByID(id); identity != nil {
   287  		return identity
   288  	}
   289  
   290  	allocatorKey, err := IdentityAllocator.GetByID(idpool.ID(id))
   291  	if err != nil {
   292  		return nil
   293  	}
   294  
   295  	if gi, ok := allocatorKey.(GlobalIdentity); ok {
   296  		return identity.NewIdentityFromLabelArray(id, gi.LabelArray)
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  // AddUserDefinedNumericIdentitySet adds all key-value pairs from the given map
   303  // to the map of user defined numeric identities and reserved identities.
   304  // The key-value pairs should map a numeric identity to a valid label.
   305  // Is not safe for concurrent use.
   306  func AddUserDefinedNumericIdentitySet(m map[string]string) error {
   307  	// Validate first
   308  	for k := range m {
   309  		ni, err := identity.ParseNumericIdentity(k)
   310  		if err != nil {
   311  			return err
   312  		}
   313  		if !identity.IsUserReservedIdentity(ni) {
   314  			return identity.ErrNotUserIdentity
   315  		}
   316  	}
   317  	for k, lbl := range m {
   318  		ni, _ := identity.ParseNumericIdentity(k)
   319  		identity.AddUserDefinedNumericIdentity(ni, lbl)
   320  		identity.AddReservedIdentity(ni, lbl)
   321  	}
   322  	return nil
   323  }