go.ligato.io/vpp-agent/v3@v3.5.0/plugins/kvscheduler/api/kv_descriptor_api.go (about)

     1  // Copyright (c) 2018 Cisco and/or its affiliates.
     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 api
    16  
    17  import (
    18  	"go.ligato.io/cn-infra/v2/idxmap"
    19  	"google.golang.org/protobuf/proto"
    20  )
    21  
    22  // Dependency references another kv pair that must exist before the associated
    23  // value can be created.
    24  type Dependency struct {
    25  	// Label should be a short human-readable string labeling the dependency.
    26  	// Must be unique in the list of dependencies for a value.
    27  	Label string
    28  
    29  	// Key of another kv pair that the associated value depends on.
    30  	// If empty, AnyOf must be defined instead.
    31  	Key string
    32  
    33  	// AnyOf defines a set of keys from which at least one must reference
    34  	// a created object for the dependency to be considered satisfied - i.e.
    35  	// **any of** the matched keys is good enough to satisfy the dependency.
    36  	// Either Key or AnyOf should be defined, but not both at the same time.
    37  	// BEWARE: AnyOf comes with more overhead than a static key dependency
    38  	// (especially when KeySelector is used without KeyPrefixes), so prefer to
    39  	// use Key whenever possible.
    40  	AnyOf AnyOfDependency
    41  }
    42  
    43  // AnyOfDependency defines a set of keys from which at least one must reference
    44  // a created object for the dependency to be considered satisfied.
    45  //
    46  // KeyPrefixes jointly select a set of keys that begin with at least one of the
    47  // defined key prefixes, potentially further filtered by KeySelector, which is
    48  // typically parsing and evaluating key suffix to check if it satisfies the
    49  // dependency (i.e. the selections of KeyPrefixes and KeySelector are
    50  // **intersected**).
    51  //
    52  // KeyPrefixes and KeySelector can be combined, but also used as standalone.
    53  // However, using only KeySelector without limiting the set of candidates using
    54  // prefixes is very costly - for the scheduling algorithm the key selector is
    55  // a black box that can potentially match any key and must be therefore checked
    56  // with every key entering the key space (i.e. linear complexity as opposed to
    57  // logarithmic complexity when the key space is suitably reduced with prefixes).
    58  type AnyOfDependency struct {
    59  	// KeyPrefixes is a list of all (longest common) key prefixes found in the
    60  	// keys satisfying the dependency. If defined (not empty), the scheduler will
    61  	// know that for a given key to match the dependency, it must begin with at
    62  	// least one of the specified prefixes.
    63  	// Keys matched by these prefixes can be further filtered with the KeySelector
    64  	// below.
    65  	KeyPrefixes []string
    66  
    67  	// KeySelector, if defined (non-nil), must return true for at least one of
    68  	// the already created keys for the dependency to be considered satisfied.
    69  	// It is recommended to narrow down the set of candidates as much as possible
    70  	// using KeyPrefixes, so that the KeySelector will not have to be called too
    71  	// many times, limiting its impact on the performance.
    72  	KeySelector KeySelector
    73  }
    74  
    75  // MetadataMapFactory can be used by descriptor to define a custom map associating
    76  // value labels with value metadata, potentially extending the basic in-memory
    77  // implementation (memNamedMapping) with secondary indexes, type-safe watch, etc.
    78  // If metadata are enabled (by WithMetadata method), the scheduler will create
    79  // an instance of the map using the provided factory during the descriptor
    80  // registration (RegisterKVDescriptor). Immediately afterwards, the mapping
    81  // is available read-only via scheduler's method GetMetadataMap. The returned
    82  // map can be then casted to the customized implementation, but it should remain
    83  // read-only (i.e. define read-only interface for the customized implementation).
    84  type MetadataMapFactory func() idxmap.NamedMappingRW
    85  
    86  // ValueOrigin is one of: FromNB, FromSB, UnknownOrigin.
    87  type ValueOrigin int
    88  
    89  const (
    90  	// UnknownOrigin is given to a retrieved value when it cannot be determined
    91  	// if the value was previously created by NB or not.
    92  	// Scheduler will then look into its history to find out if the value was
    93  	// ever managed by NB to determine the origin heuristically.
    94  	UnknownOrigin ValueOrigin = iota
    95  
    96  	// FromNB marks value created via NB transaction.
    97  	FromNB
    98  
    99  	// FromSB marks value not managed by NB - i.e. created automatically or
   100  	// externally in SB.
   101  	FromSB
   102  )
   103  
   104  // String converts ValueOrigin to string.
   105  func (vo ValueOrigin) String() string {
   106  	switch vo {
   107  	case FromNB:
   108  		return "from-NB"
   109  	case FromSB:
   110  		return "from-SB"
   111  	default:
   112  		return "unknown"
   113  	}
   114  }
   115  
   116  // KVDescriptor teaches KVScheduler how to CRUD values under keys matched
   117  // by KeySelector().
   118  //
   119  // Every SB component should define one or more descriptors to cover all
   120  // (non-property) keys under its management. The descriptor is what in essence
   121  // gives meaning to individual key-value pairs. The list of available keys and
   122  // their purpose should be properly documented so that clients from the NB plane
   123  // can use them correctly. The scheduler does not care what CRUD methods do,
   124  // it only needs to call the right callbacks at the right time.
   125  //
   126  // Every key-value pair must have at most one descriptor associated with it.
   127  // NB base value without descriptor is considered unimplemented and will never
   128  // be created.
   129  // On the other hand, derived value is allowed to have no descriptor associated
   130  // with it. Typically, properties of base values are implemented as derived
   131  // (often empty) values without attached SB operations, used as targets for
   132  // dependencies.
   133  type KVDescriptor struct {
   134  	// Name of the descriptor unique across all registered descriptors.
   135  	Name string
   136  
   137  	// TODO: replace KeySelector, KeyLabel & NBKeyPrefix with methods from
   138  	// models.Spec.
   139  
   140  	// KeySelector selects keys described by this descriptor.
   141  	KeySelector KeySelector
   142  
   143  	// TODO: obsolete, remove once Orchestrator is completed
   144  	// ValueTypeName defines name of the proto.Message type used to represent
   145  	// described values. This attribute is mandatory, otherwise LazyValue-s
   146  	// received from NB (e.g. datasync package) cannot be un-marshalled.
   147  	// Note: proto Messages are registered against this type name in the generated
   148  	// code using proto.RegisterType().
   149  	ValueTypeName string
   150  
   151  	// KeyLabel can be *optionally* defined to provide a *shorter* value
   152  	// identifier, that, unlike the original key, only needs to be unique in the
   153  	// key scope of the descriptor and not necessarily in the entire key space.
   154  	// If defined, key label will be used as value identifier in the metadata map
   155  	// and in the non-verbose logs.
   156  	KeyLabel func(key string) string
   157  
   158  	// NBKeyPrefix is a key prefix that the scheduler should watch
   159  	// in NB to receive all NB-values described by this descriptor.
   160  	// The key space defined by NBKeyPrefix may cover more than KeySelector
   161  	// selects - the scheduler will filter the received values and pass
   162  	// to the descriptor only those that are really chosen by KeySelector.
   163  	// The opposite may be true as well - KeySelector may select some extra
   164  	// SB-only values, which the scheduler will not watch for in NB. Furthermore,
   165  	// the keys may already be requested for watching by another descriptor
   166  	// within the same plugin and in such case it is not needed to mention the
   167  	// same prefix again.
   168  	NBKeyPrefix string
   169  
   170  	// ValueComparator can be *optionally* provided to customize comparision
   171  	// of values for equality.
   172  	// Scheduler compares values to determine if Update operation is really
   173  	// needed.
   174  	// For NB values, <oldValue> was either previously set by NB or refreshed
   175  	// from SB, whereas <newValue> is a new value to be applied by NB.
   176  	ValueComparator func(key string, oldValue, newValue proto.Message) bool
   177  
   178  	// WithMetadata tells scheduler whether to enable metadata - run-time,
   179  	// descriptor-owned, scheduler-opaque, data carried alongside a created
   180  	// (non-derived) value.
   181  	// If enabled, the scheduler will maintain a map between key (-label, if
   182  	// KeyLabel is defined) and the associated metadata.
   183  	// If <WithMetadata> is false, metadata returned by Create will be ignored
   184  	// and other methods will receive nil metadata.
   185  	WithMetadata bool
   186  
   187  	// MetadataMapFactory can be used to provide a customized map implementation
   188  	// for value metadata, possibly extended with secondary lookups.
   189  	// If not defined, the scheduler will use the bare NamedMapping from
   190  	// the idxmap package.
   191  	MetadataMapFactory MetadataMapFactory
   192  
   193  	// Validate value handler (optional).
   194  	// Validate is called for every new value before it is Created or Updated.
   195  	// If the validations fails (returned <err> is non-nil), the scheduler will
   196  	// mark the value as invalid and will not attempt to apply it.
   197  	// The descriptor can further specify which field(s) are not valid
   198  	// by wrapping the validation error together with a slice of invalid fields
   199  	// using the error InvalidValueError (see errors.go).
   200  	Validate func(key string, value proto.Message) error
   201  
   202  	// Create new value handler.
   203  	// For non-derived values, descriptor may return metadata to associate with
   204  	// the value.
   205  	// For derived values, Create+Delete+Update are optional. Typically, properties
   206  	// of base values are implemented as derived (often empty) values without
   207  	// attached SB operations, used as targets for dependencies.
   208  	Create func(key string, value proto.Message) (metadata Metadata, err error)
   209  
   210  	// Delete value handler.
   211  	// If Create is defined, Delete handler must be provided as well.
   212  	Delete func(key string, value proto.Message, metadata Metadata) error
   213  
   214  	// Update value handler.
   215  	// The handler is optional - if not defined, value change will be carried out
   216  	// via full re-creation (Delete followed by Create with the new value).
   217  	// <newMetadata> can re-use the <oldMetadata>.
   218  	Update func(key string, oldValue, newValue proto.Message, oldMetadata Metadata) (newMetadata Metadata, err error)
   219  
   220  	// UpdateWithRecreate can be defined to tell the scheduler if going from
   221  	// <oldValue> to <newValue> requires the value to be completely re-created
   222  	// with Delete+Create handlers.
   223  	// If not defined, KVScheduler will decide based on the (un)availability
   224  	// of the Update operation - if provided, it is assumed that any change
   225  	// can be applied incrementally, otherwise a full re-creation is the only way
   226  	// to go.
   227  	UpdateWithRecreate func(key string, oldValue, newValue proto.Message, metadata Metadata) bool
   228  
   229  	// Retrieve should return all non-derived values described by this descriptor
   230  	// that *really* exist in the southbound plane (and not what the current
   231  	// scheduler's view of SB is). Derived value will get automatically created
   232  	// using the method DerivedValues(). If some non-derived value doesn't
   233  	// actually exist, it shouldn't be returned by DerivedValues() for the
   234  	// retrieved base value!
   235  	// <correlate> represents the non-derived values currently created
   236  	// as viewed from the northbound/scheduler point of view:
   237  	//   -> startup resync: <correlate> = values received from NB to be applied
   238  	//   -> run-time/downstream resync: <correlate> = values applied according
   239  	//      to the in-memory kv-store (scheduler's view of SB)
   240  	//
   241  	// The callback is optional - if not defined, it is assumed that descriptor
   242  	// is not able to read the current SB state and thus refresh cannot be
   243  	// performed for its kv-pairs.
   244  	Retrieve func(correlate []KVWithMetadata) ([]KVWithMetadata, error)
   245  
   246  	// IsRetriableFailure tells scheduler if the given error, returned by one
   247  	// of Create/Delete/Update handlers, will always be returned for the
   248  	// the same value (non-retriable) or if the value can be theoretically
   249  	// fixed merely by repeating the operation.
   250  	// If the callback is not defined, every error will be considered retriable.
   251  	IsRetriableFailure func(err error) bool
   252  
   253  	// DerivedValues returns ("derived") values solely inferred from the current
   254  	// state of this ("base") value. Derived values cannot be changed by NB
   255  	// transaction.
   256  	// While their state and existence is bound to the state of their base value,
   257  	// they are allowed to have their own descriptors.
   258  	//
   259  	// Typically, derived value represents the base value's properties (that
   260  	// other kv pairs may depend on), or extra actions taken when additional
   261  	// dependencies are met, but otherwise not blocking the base
   262  	// value from being created.
   263  	//
   264  	// The callback is optional - if not defined, there will be no values derived
   265  	// from kv-pairs of the descriptor.
   266  	DerivedValues func(key string, value proto.Message) []KeyValuePair
   267  
   268  	// Dependencies are keys that must already exist for the value to be created.
   269  	// Conversely, if a dependency is to be removed, all values that depend on it
   270  	// are deleted first and cached for a potential future re-creation.
   271  	// Dependencies returned in the list are AND-ed.
   272  	// The callback is optional - if not defined, the kv-pairs of the descriptor
   273  	// are assumed to have no dependencies.
   274  	Dependencies func(key string, value proto.Message) []Dependency
   275  
   276  	// RetrieveDependencies is a list of descriptors whose values are needed
   277  	// and should be already retrieved prior to calling Retrieve for this
   278  	// descriptor.
   279  	// Metadata for values already retrieved are available via GetMetadataMap().
   280  	// TODO: define dependencies as a slice of models, not descriptors.
   281  	RetrieveDependencies []string /* descriptor name */
   282  }