gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/go-control-plane/pkg/server/delta/v3/server.go (about) 1 package delta 2 3 import ( 4 "context" 5 "errors" 6 "gitee.com/ks-custle/core-gm/go-control-plane/pkg/cache/v3" 7 "gitee.com/ks-custle/core-gm/go-control-plane/pkg/resource/v3" 8 "gitee.com/ks-custle/core-gm/go-control-plane/pkg/server/stream/v3" 9 "strconv" 10 "sync/atomic" 11 12 "gitee.com/ks-custle/core-gm/grpc/codes" 13 "gitee.com/ks-custle/core-gm/grpc/status" 14 15 core "gitee.com/ks-custle/core-gm/go-control-plane/envoy/config/core/v3" 16 discovery "gitee.com/ks-custle/core-gm/go-control-plane/envoy/service/discovery/v3" 17 ) 18 19 // Server is a wrapper interface which is meant to hold the proper stream handler for each xDS protocol. 20 type Server interface { 21 DeltaStreamHandler(stream stream.DeltaStream, typeURL string) error 22 } 23 24 type Callbacks interface { 25 // OnDeltaStreamOpen is called once an incremental xDS stream is open with a stream ID and the type URL (or "" for ADS). 26 // Returning an error will end processing and close the stream. OnStreamClosed will still be called. 27 OnDeltaStreamOpen(context.Context, int64, string) error 28 // OnDeltaStreamClosed is called immediately prior to closing an xDS stream with a stream ID. 29 OnDeltaStreamClosed(int64) 30 // OnStreamDeltaRequest is called once a request is received on a stream. 31 // Returning an error will end processing and close the stream. OnStreamClosed will still be called. 32 OnStreamDeltaRequest(int64, *discovery.DeltaDiscoveryRequest) error 33 // OnStreamDeltaResponse is called immediately prior to sending a response on a stream. 34 OnStreamDeltaResponse(int64, *discovery.DeltaDiscoveryRequest, *discovery.DeltaDiscoveryResponse) 35 } 36 37 var deltaErrorResponse = &cache.RawDeltaResponse{} 38 39 type server struct { 40 cache cache.ConfigWatcher 41 callbacks Callbacks 42 43 // total stream count for counting bi-di streams 44 streamCount int64 45 ctx context.Context 46 } 47 48 // NewServer creates a delta xDS specific server which utilizes a ConfigWatcher and delta Callbacks. 49 func NewServer(ctx context.Context, config cache.ConfigWatcher, callbacks Callbacks) Server { 50 return &server{ 51 cache: config, 52 callbacks: callbacks, 53 ctx: ctx, 54 } 55 } 56 57 func (s *server) processDelta(str stream.DeltaStream, reqCh <-chan *discovery.DeltaDiscoveryRequest, defaultTypeURL string) error { 58 streamID := atomic.AddInt64(&s.streamCount, 1) 59 60 // streamNonce holds a unique nonce for req-resp pairs per xDS stream. 61 var streamNonce int64 62 63 // a collection of stack allocated watches per request type 64 watches := newWatches() 65 66 defer func() { 67 watches.Cancel() 68 if s.callbacks != nil { 69 s.callbacks.OnDeltaStreamClosed(streamID) 70 } 71 }() 72 73 // Sends a response, returns the new stream nonce 74 send := func(resp cache.DeltaResponse) (string, error) { 75 if resp == nil { 76 return "", errors.New("missing response") 77 } 78 79 response, err := resp.GetDeltaDiscoveryResponse() 80 if err != nil { 81 return "", err 82 } 83 84 streamNonce = streamNonce + 1 85 response.Nonce = strconv.FormatInt(streamNonce, 10) 86 if s.callbacks != nil { 87 s.callbacks.OnStreamDeltaResponse(streamID, resp.GetDeltaRequest(), response) 88 } 89 90 return response.Nonce, str.Send(response) 91 } 92 93 if s.callbacks != nil { 94 if err := s.callbacks.OnDeltaStreamOpen(str.Context(), streamID, defaultTypeURL); err != nil { 95 return err 96 } 97 } 98 99 var node = &core.Node{} 100 for { 101 select { 102 case <-s.ctx.Done(): 103 return nil 104 case resp, more := <-watches.deltaMuxedResponses: 105 if !more { 106 break 107 } 108 109 typ := resp.GetDeltaRequest().GetTypeUrl() 110 if resp == deltaErrorResponse { 111 return status.Errorf(codes.Unavailable, typ+" watch failed") 112 } 113 114 nonce, err := send(resp) 115 if err != nil { 116 return err 117 } 118 119 watch := watches.deltaWatches[typ] 120 watch.nonce = nonce 121 122 watch.state.SetResourceVersions(resp.GetNextVersionMap()) 123 watches.deltaWatches[typ] = watch 124 case req, more := <-reqCh: 125 // input stream ended or errored out 126 if !more { 127 return nil 128 } 129 if req == nil { 130 return status.Errorf(codes.Unavailable, "empty request") 131 } 132 133 if s.callbacks != nil { 134 if err := s.callbacks.OnStreamDeltaRequest(streamID, req); err != nil { 135 return err 136 } 137 } 138 139 // The node information might only be set on the first incoming delta discovery request, so store it here so we can 140 // reset it on subsequent requests that omit it. 141 if req.Node != nil { 142 node = req.Node 143 } else { 144 req.Node = node 145 } 146 147 // type URL is required for ADS but is implicit for any other xDS stream 148 if defaultTypeURL == resource.AnyType { 149 if req.TypeUrl == "" { 150 return status.Errorf(codes.InvalidArgument, "type URL is required for ADS") 151 } 152 } else if req.TypeUrl == "" { 153 req.TypeUrl = defaultTypeURL 154 } 155 156 typeURL := req.GetTypeUrl() 157 158 // cancel existing watch to (re-)request a newer version 159 watch, ok := watches.deltaWatches[typeURL] 160 if !ok { 161 // Initialize the state of the stream. 162 // Since there was no previous state, we know we're handling the first request of this type 163 // so we set the initial resource versions if we have any, and also signal if this stream is in wildcard mode. 164 watch.state = stream.NewStreamState(len(req.GetResourceNamesSubscribe()) == 0, req.GetInitialResourceVersions()) 165 } else { 166 watch.Cancel() 167 } 168 169 s.subscribe(req.GetResourceNamesSubscribe(), watch.state.GetResourceVersions()) 170 s.unsubscribe(req.GetResourceNamesUnsubscribe(), watch.state.GetResourceVersions()) 171 172 watch.responses = make(chan cache.DeltaResponse, 1) 173 watch.cancel = s.cache.CreateDeltaWatch(req, watch.state, watch.responses) 174 watches.deltaWatches[typeURL] = watch 175 176 go func() { 177 resp, more := <-watch.responses 178 if more { 179 watches.deltaMuxedResponses <- resp 180 } 181 }() 182 } 183 } 184 } 185 186 func (s *server) DeltaStreamHandler(str stream.DeltaStream, typeURL string) error { 187 // a channel for receiving incoming delta requests 188 reqCh := make(chan *discovery.DeltaDiscoveryRequest) 189 190 // we need to concurrently handle incoming requests since we kick off processDelta as a return 191 go func() { 192 for { 193 select { 194 case <-str.Context().Done(): 195 close(reqCh) 196 return 197 default: 198 req, err := str.Recv() 199 if err != nil { 200 close(reqCh) 201 return 202 } 203 204 reqCh <- req 205 } 206 } 207 }() 208 209 return s.processDelta(str, reqCh, typeURL) 210 } 211 212 // When we subscribe, we just want to make the cache know we are subscribing to a resource. 213 // Providing a name with an empty version is enough to make that happen. 214 func (s *server) subscribe(resources []string, sv map[string]string) { 215 for _, res := range resources { 216 sv[res] = "" 217 } 218 } 219 220 // Unsubscriptions remove resources from the stream state to 221 // indicate to the cache that we don't care about the resource anymore 222 func (s *server) unsubscribe(resources []string, sv map[string]string) { 223 for _, res := range resources { 224 delete(sv, res) 225 } 226 }