github.com/annwntech/go-micro/v2@v2.9.5/util/kubernetes/client/watch.go (about) 1 package client 2 3 import ( 4 "bufio" 5 "context" 6 "encoding/json" 7 "errors" 8 "net/http" 9 10 "github.com/annwntech/go-micro/v2/util/kubernetes/api" 11 ) 12 13 const ( 14 // EventTypes used 15 Added EventType = "ADDED" 16 Modified EventType = "MODIFIED" 17 Deleted EventType = "DELETED" 18 Error EventType = "ERROR" 19 ) 20 21 // Watcher is used to watch for events 22 type Watcher interface { 23 // A channel of events 24 Chan() <-chan Event 25 // Stop the watcher 26 Stop() 27 } 28 29 // EventType defines the possible types of events. 30 type EventType string 31 32 // Event represents a single event to a watched resource. 33 type Event struct { 34 Type EventType `json:"type"` 35 Object json.RawMessage `json:"object"` 36 } 37 38 // bodyWatcher scans the body of a request for chunks 39 type bodyWatcher struct { 40 results chan Event 41 cancel func() 42 stop chan bool 43 res *http.Response 44 req *api.Request 45 } 46 47 // Changes returns the results channel 48 func (wr *bodyWatcher) Chan() <-chan Event { 49 return wr.results 50 } 51 52 // Stop cancels the request 53 func (wr *bodyWatcher) Stop() { 54 select { 55 case <-wr.stop: 56 return 57 default: 58 // cancel the request 59 wr.cancel() 60 // stop the watcher 61 close(wr.stop) 62 } 63 } 64 65 func (wr *bodyWatcher) stream() { 66 reader := bufio.NewReader(wr.res.Body) 67 68 go func() { 69 for { 70 // read a line 71 b, err := reader.ReadBytes('\n') 72 if err != nil { 73 return 74 } 75 76 // send the event 77 var event Event 78 if err := json.Unmarshal(b, &event); err != nil { 79 continue 80 } 81 82 select { 83 case <-wr.stop: 84 return 85 case wr.results <- event: 86 } 87 } 88 }() 89 } 90 91 // newWatcher creates a k8s body watcher for 92 // a given http request 93 func newWatcher(req *api.Request) (Watcher, error) { 94 // set request context so we can cancel the request 95 ctx, cancel := context.WithCancel(context.Background()) 96 req.Context(ctx) 97 98 // do the raw request 99 res, err := req.Raw() 100 if err != nil { 101 cancel() 102 return nil, err 103 } 104 105 if res.StatusCode < 200 || res.StatusCode >= 300 { 106 cancel() 107 // close the response body 108 res.Body.Close() 109 // return an error 110 return nil, errors.New(res.Request.URL.String() + ": " + res.Status) 111 } 112 113 wr := &bodyWatcher{ 114 results: make(chan Event), 115 stop: make(chan bool), 116 cancel: cancel, 117 req: req, 118 res: res, 119 } 120 121 go wr.stream() 122 123 return wr, nil 124 }