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 ```