github.com/Axway/agent-sdk@v1.1.101/pkg/watchmanager/README.md (about)

     1  # Watch Manager
     2  The watch library provides the ability to subscribe to API Server resources over gRPC to Amplify Central on a configured filter defined using the WatchTopic resource.
     3  
     4  ## Table of Contents
     5  - [Overview](#Overview)
     6  - [Watch manager](#Watch-manager)
     7      - [Watch client configuration](#Watch-client-configuration)
     8      - [Watch client options](#Watch-client-options)
     9      - [Registration](#Registration)
    10  - [Client Example](#Client-Example)
    11  
    12  ## Overview
    13  Amplify Central provides a gRPC based watch service that provides the ability to register a subscription for API server resources on a bi-directional gRPC stream. The subscription is based on an API server resource called WatchTopic, and it defines the set of filters. The Amplify Central watch service uses the WatchTopic to match the API server resource event before pushing them to subscribed clients. 
    14  
    15  #### WatchTopic Example
    16  ```yaml
    17  group: management
    18  apiVersion: v1alpha1
    19  kind: WatchTopic
    20  name: sample-watch-topic
    21  title: sample-watch-topic
    22  spec:
    23    filters:
    24      - kind: APIService
    25        name: '*'
    26        type:
    27          - created
    28          - updated
    29          - deleted
    30        group: management
    31        scope:
    32          kind: Environment
    33          name: sample-env
    34      - kind: APIServiceInstance
    35        name: '*'
    36        type:
    37          - created
    38          - updated
    39          - deleted
    40        group: management
    41        scope:
    42          kind: Environment
    43          name: sample-env
    44    description: >-
    45      Sample watch topic in sample-env environment.
    46  ```
    47  
    48  #### Creating WatchTopic resource
    49  Use the Axway Central CLI to create the WatchTopic resource. Create a file with a YAML or JSON definition for the WatchTopic resource specifying the filters for the resources to subscribe to (see above example).
    50  
    51  Use the following command to authenticate with your Amplify platform credentials
    52  ```bash
    53  axway auth login
    54  ```
    55  
    56  Use the command below to create the WatchTopic resource.
    57  ```shell
    58  axway central apply -f <filePath-for-watch-topic-resource>
    59  ```
    60  
    61  Use the following command to verify the WatchTopic resource and note the value for "metadata.selfLink" property from the output of the above command. The WatchTopic self link will be used while registering the watch subscription
    62  ```shell
    63  axway central get watchtopic <logical-name-of-watch-topic-resource> -o yaml
    64  ```
    65  
    66  Once the WatchTopic resources is defined in API server, the Amplify Central watch service starts to monitor API resource events, and will deliver the event in real time over the subscribed gRPC connections and persists the event to allow the clients to retrieve them at a point of time based on the sequence identifier. This helps the client to catch up with any API server resource events that were missed while the client was not running.
    67  
    68  ## Watch manager
    69  The watch manager library provides an interface to create and manage the client communication with Amplify Central. The library creates the gRPC connection based on the provided configuration, and the watch options that can be setup while creating the client. The watch client interface provided by the library allows registering the watch subscription by using the provided WatchTopic self link. 
    70  
    71  For registration with the watch service, the watch client uses the provided token getter to retrieve the JWT token that the client uses to call the Subscribe gRPC endpoint by including the token in the request metadata. The watch service uses the metadata to authorize the subscription request and opens a long-lived bi-directional stream connection with the client on successful authorization. The bi-directional stream is then used by the client to refresh the token when the token is about to expire to keep the connection active.
    72  
    73  The client manages the long-lived gRPC stream connection by sending keep alive pings at a set interval. The client transport waits for a response from the watch service to know that the stream is alive. If a response is not received within the timeout period, then the transport is disconnected.
    74  
    75  ### Watch client configuration
    76  The watch manager library requires the following configuration to establish the connection with Amplify Central watch service.
    77  
    78  ```golang
    79  type Config struct {
    80  	Host        string
    81  	Port        uint32
    82  	TenantID    string
    83  	TokenGetter TokenGetter
    84  }
    85  ```
    86  
    87  - Host: identifies the host for Amplify Central watch service (US region: apicentral.axway.com, EU region: central.eu-fr.axway.com)
    88  - Port: identifies the port for Amplify Central watch service (443)
    89  - TenantID: Amplify platform organization ID
    90  - TokenGetter: interface to retrieve AxwayID token
    91  
    92  ### Watch client options
    93  The watch manager library provides following set of options that the implementation can choose to use for setting up/overriding the properties for the gRPC stream connection.
    94  
    95  - WithLogger - The option method takes *logrus.Entry as an argument and allows overriding the client stream logger.
    96  - WithTLSConfig - Receives *tls.Config as an argument to override the default TLS configuration. 
    97  - WithKeepAlive - The method takes a keep alive ping interval and timeout to override the default values.
    98  - WithProxy - The method takes the proxy url to be used for establishing the gRPC connection via a specified proxy
    99  - WithSyncEvents - The method takes instance for following interface. If setup, the GetSequence method is invoked on successful gRPC connection to fetch events after the sequence id returned by the method.
   100      ```golang
   101      type SequenceProvider interface {
   102          GetSequence() int64
   103      }
   104      ```
   105  
   106  ### Registration
   107  To create a new watch manager, use the following method from the watchmanager package with the watch configuration and set of options
   108  ```golang
   109  func New(cfg *Config, opts ...Option) (Manager, error)
   110  ```
   111  
   112  The method create a new watch manager client and returns the following interface to allow implementation to manage the gRPC stream
   113  ```golang
   114  type Manager interface {
   115  	RegisterWatch(topicSelfLink string, eventChan chan *proto.Event, errChan chan error) (string, error)
   116  	CloseWatch(id string) error
   117  	CloseConn()
   118  	Status() bool
   119  }
   120  ```
   121  
   122  The client can call the *RegisterWatch* method with the topic self link and a set of channels to receive events and errors.
   123  
   124  When the client initiates the subscription request, it calls the sequence getter if configured to get the last known sequence identifier of the resource event that the implementation received. On successful subscription request, the client places an API call to Amplify Central watch service to fetch the events that were missed while the gRPC watch stream connection was not active.
   125  
   126  When the client receives an event from the gRPC stream (or fetched by API call) it hands over the events to the implementation by writing them to the event channel configured while registering the watch subscription. In case the client receives any error on gRPC stream connection, the error is written to an error channel configured while registering the watch subscription.
   127  
   128  Below is the structure of the event that is received by the Amplify Central watch service(refer [./proto/watch.pb.go](./proto/watch.pb.go) for more detail)
   129  ```golang
   130  
   131  type Event struct {
   132      // Event ID
   133      Id            string
   134      // Event Time
   135      Time          string
   136      // Event Version
   137      Version       string
   138      // Product raising the event
   139      Product       string
   140      // Event correlation ID 
   141      CorrelationId string
   142      // Organization associated to the event
   143      Organization  *Organization
   144      // Event Type
   145      Type          Event_Type
   146      // Event payload representing the API server resource instance
   147      Payload       *ResourceInstance
   148      // Event metadata holding watch topic id, self link, event sequence ID and sub resource name (if event raised for sub resource) 
   149      Metadata      *EventMeta
   150  }
   151  
   152  ```
   153  
   154  ## Client Example
   155  ```golang
   156  
   157  type sequenceManager struct {
   158      ...
   159  }
   160  
   161  func (s *sequenceManager) GetSequence() int64 {
   162      ...
   163      // get last known sequence ID to fetch event while the client was down
   164      ...  
   165      return sequenceID
   166  }
   167  
   168  
   169  type AxwayIDTokenManager struct {
   170      ...
   171  }
   172  
   173  func (a *AxwayIDTokenManager) GetToken() (string, error)
   174      // fetch token
   175      return token, err
   176  }
   177  
   178  func defaultTLSConfig() *tls.Config {
   179  	return &tls.Config{
   180  		MinVersion: tls.VersionTLS12,
   181  		CipherSuites: []uint16{
   182  			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   183  			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   184  			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
   185  			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
   186  			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   187  			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   188  			tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
   189  			tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
   190  		},
   191  	}
   192  }
   193  
   194  func startWatch(tenantID string, host string, port uint32, topicSelfLink string, proxyUrl string) error {
   195      /**
   196      * 1. Create token getter that will return the AxwayID JWT token for authorizing the client connection
   197      */
   198      tokenManager := &AxwayIDTokenManager{}
   199      // Alternatively use SDK token getter component by calling NewTokenAuth from 
   200      // github.com/Axway/agent-sdk/pkg/apic/auth package
   201  
   202      /**
   203      * 2. Setup watch config
   204      */
   205      cfg := &watchmanager.Config{
   206          Host:        host,
   207          Port:        port,
   208          TenantID:    tenantID,
   209          TokenGetter: tokenManager,
   210      }
   211  
   212      /**
   213      * 3. Create watch client using supported options
   214      */
   215      wm, err := watchmanager.New(cfg,
   216          watchmanager.WithLogger(entry),
   217          watchmanager.WithTLSConfig(defaultTLSConfig()),
   218          watchmanager.WithKeepAlive(30*time.Second, 10*time.Second),
   219          watchmanager.WithProxy(proxyUrl),
   220          watchmanager.WithSyncEvents(getSequenceManager()),
   221      )
   222      if err != nil {
   223          return err
   224      }
   225  
   226      /**
   227      * 4. Create channels to receive event and error
   228      */
   229      eventChannel, errCh := make(chan *proto.Event), make(chan error)
   230  
   231      /**
   232      * 5. Register the watch subscription
   233      */
   234      subscriptionID, err := wm.RegisterWatch(topicSelfLink, eventChannel, errCh)
   235      if err != nil {
   236          log.Error(err)
   237          return
   238      }
   239  
   240      log.Infof("watch subscription (%s) registered successfully", subscriptionID)
   241  
   242      /**
   243      * 6. Start to process event and error received on channel
   244      */
   245      for {
   246          select {
   247          case err = <-errCh:
   248              log.Error(err)
   249              wm.CloseWatch(subscriptionID)
   250              return
   251          case event := <-eventChannel:
   252              bts, _ := json.MarshalIndent(event, "", "  ")
   253              log.Info(string(bts))
   254          }
   255      }
   256  }
   257  
   258  ```