github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/events.go (about)

     1  /*
     2  Copyright 2020 Gravitational, Inc.
     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 types
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/gravitational/trace"
    24  )
    25  
    26  // String returns text description of this event
    27  func (r Event) String() string {
    28  	if r.Type == OpDelete {
    29  		return fmt.Sprintf("%v(%v/%v)", r.Type, r.Resource.GetKind(), r.Resource.GetSubKind())
    30  	}
    31  	return fmt.Sprintf("%v(%v)", r.Type, r.Resource)
    32  }
    33  
    34  // Event represents an event that happened in the backend
    35  type Event struct {
    36  	// Type is the event type
    37  	Type OpType
    38  	// Resource is a modified or deleted resource
    39  	// in case of deleted resources, only resource header
    40  	// will be provided
    41  	Resource Resource
    42  }
    43  
    44  // OpType specifies operation type
    45  type OpType int
    46  
    47  const (
    48  	// OpUnreliable is used to indicate the event stream has become unreliable
    49  	// for maintaining an up-to-date view of the data.
    50  	OpUnreliable OpType = iota - 2
    51  	// OpInvalid is returned for invalid operations
    52  	OpInvalid
    53  	// OpInit is returned by the system whenever the system
    54  	// is initialized, init operation is always sent
    55  	// as a first event over the channel, so the client
    56  	// can verify that watch has been established.
    57  	OpInit
    58  	// OpPut is returned for Put events
    59  	OpPut
    60  	// OpDelete is returned for Delete events
    61  	OpDelete
    62  	// OpGet is used for tracking, not present in the event stream
    63  	OpGet
    64  )
    65  
    66  // String returns user-friendly description of the operation
    67  func (o OpType) String() string {
    68  	switch o {
    69  	case OpUnreliable:
    70  		return "Unreliable"
    71  	case OpInvalid:
    72  		return "Invalid"
    73  	case OpInit:
    74  		return "Init"
    75  	case OpPut:
    76  		return "Put"
    77  	case OpDelete:
    78  		return "Delete"
    79  	case OpGet:
    80  		return "Get"
    81  	default:
    82  		return "unknown"
    83  	}
    84  }
    85  
    86  // Watch sets up watch on the event
    87  type Watch struct {
    88  	// Name is used for debugging purposes
    89  	Name string
    90  
    91  	// Kinds specifies kinds of objects to watch
    92  	// and whether to load secret data for them
    93  	Kinds []WatchKind
    94  
    95  	// QueueSize is an optional queue size
    96  	QueueSize int
    97  
    98  	// MetricComponent is used for reporting
    99  	MetricComponent string
   100  
   101  	// AllowPartialSuccess enables a mode in which a watch will succeed if some of the requested kinds aren't available.
   102  	// When this is set, the client must inspect the WatchStatus resource attached to the first OpInit event emitted
   103  	// by the watcher for a list of kinds confirmed by the event source. Kinds requested but omitted from the confirmation
   104  	// will not be included in the event stream.
   105  	// If AllowPartialSuccess was set, but OpInit doesn't have a resource attached, it means that the event source
   106  	// doesn't support partial success and all requested resource kinds should be considered confirmed.
   107  	AllowPartialSuccess bool
   108  }
   109  
   110  // Matches attempts to determine if the supplied event matches
   111  // this WatchKind.  If the WatchKind is misconfigured, or the
   112  // event appears malformed, an error is returned.
   113  func (kind WatchKind) Matches(e Event) (bool, error) {
   114  	if kind.Kind != e.Resource.GetKind() {
   115  		return false, nil
   116  	}
   117  	if kind.Name != "" && kind.Name != e.Resource.GetName() {
   118  		return false, nil
   119  	}
   120  	// we don't have a good model for filtering non-put events,
   121  	// so only apply filters to OpPut events.
   122  	if len(kind.Filter) > 0 && e.Type == OpPut {
   123  		switch res := e.Resource.(type) {
   124  		case AccessRequest:
   125  			var filter AccessRequestFilter
   126  			if err := filter.FromMap(kind.Filter); err != nil {
   127  				return false, trace.Wrap(err)
   128  			}
   129  			return filter.Match(res), nil
   130  		case WebSession:
   131  			var filter WebSessionFilter
   132  			if err := filter.FromMap(kind.Filter); err != nil {
   133  				return false, trace.Wrap(err)
   134  			}
   135  			return filter.Match(res), nil
   136  		case Lock:
   137  			var target LockTarget
   138  			if err := target.FromMap(kind.Filter); err != nil {
   139  				return false, trace.Wrap(err)
   140  			}
   141  			return target.Match(res), nil
   142  		case CertAuthority:
   143  			var filter CertAuthorityFilter
   144  			filter.FromMap(kind.Filter)
   145  			return filter.Match(res), nil
   146  		case *HeadlessAuthentication:
   147  			var filter HeadlessAuthenticationFilter
   148  			filter.FromMap(kind.Filter)
   149  			return filter.Match(res), nil
   150  		default:
   151  			// we don't know about this filter, let the event through
   152  		}
   153  	}
   154  	return true, nil
   155  }
   156  
   157  // IsTrivial returns true iff the WatchKind only specifies a Kind but no other field.
   158  func (kind WatchKind) IsTrivial() bool {
   159  	return kind.SubKind == "" && kind.Name == "" && kind.Version == "" && !kind.LoadSecrets && len(kind.Filter) == 0
   160  }
   161  
   162  // Contains determines whether kind (receiver) targets exactly the same or a wider scope of events as the given subset kind.
   163  // Generally this means that if kind specifies a filter, its subset must have exactly the same or a narrower one.
   164  // Currently, does not take resource versions into account.
   165  func (kind WatchKind) Contains(subset WatchKind) bool {
   166  	// kind and subkind must always be equal
   167  	if kind.Kind != subset.Kind || kind.SubKind != subset.SubKind {
   168  		return false
   169  	}
   170  
   171  	if kind.Name != "" && kind.Name != subset.Name {
   172  		return false
   173  	}
   174  
   175  	if !kind.LoadSecrets && subset.LoadSecrets {
   176  		return false
   177  	}
   178  
   179  	if kind.Kind == KindCertAuthority {
   180  		var a, b CertAuthorityFilter
   181  		a.FromMap(kind.Filter)
   182  		b.FromMap(subset.Filter)
   183  		return a.Contains(b)
   184  	}
   185  
   186  	for k, v := range kind.Filter {
   187  		if subset.Filter[k] != v {
   188  			return false
   189  		}
   190  	}
   191  
   192  	return true
   193  }
   194  
   195  // Events returns new events interface
   196  type Events interface {
   197  	// NewWatcher returns a new event watcher
   198  	NewWatcher(ctx context.Context, watch Watch) (Watcher, error)
   199  }
   200  
   201  // Watcher returns watcher
   202  type Watcher interface {
   203  	// Events returns channel with events
   204  	Events() <-chan Event
   205  
   206  	// Done returns the channel signaling the closure
   207  	Done() <-chan struct{}
   208  
   209  	// Close closes the watcher and releases
   210  	// all associated resources
   211  	Close() error
   212  
   213  	// Error returns error associated with watcher
   214  	Error() error
   215  }