github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/runtime/kubernetes/client/watch.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); 2 // you may not use this file except in compliance with the License. 3 // You may obtain a copy of the License at 4 // 5 // https://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, 9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 // See the License for the specific language governing permissions and 11 // limitations under the License. 12 // 13 // Original source: github.com/micro/go-micro/v3/util/kubernetes/client/watch.go 14 15 package client 16 17 import ( 18 "bufio" 19 "context" 20 "encoding/json" 21 "errors" 22 "net/http" 23 24 "github.com/tickoalcantara12/micro/v3/service/runtime/kubernetes/api" 25 ) 26 27 const ( 28 // EventTypes used 29 Added EventType = "ADDED" 30 Modified EventType = "MODIFIED" 31 Deleted EventType = "DELETED" 32 Error EventType = "ERROR" 33 ) 34 35 // Watcher is used to watch for events 36 type Watcher interface { 37 // A channel of events 38 Chan() <-chan Event 39 // Stop the watcher 40 Stop() 41 } 42 43 // EventType defines the possible types of events. 44 type EventType string 45 46 // Event represents a single event to a watched resource. 47 type Event struct { 48 Type EventType `json:"type"` 49 Object json.RawMessage `json:"object"` 50 } 51 52 // bodyWatcher scans the body of a request for chunks 53 type bodyWatcher struct { 54 results chan Event 55 cancel func() 56 stop chan bool 57 res *http.Response 58 req *api.Request 59 } 60 61 // Changes returns the results channel 62 func (wr *bodyWatcher) Chan() <-chan Event { 63 return wr.results 64 } 65 66 // Stop cancels the request 67 func (wr *bodyWatcher) Stop() { 68 select { 69 case <-wr.stop: 70 return 71 default: 72 // cancel the request 73 wr.cancel() 74 // stop the watcher 75 close(wr.stop) 76 } 77 } 78 79 func (wr *bodyWatcher) stream() { 80 reader := bufio.NewReader(wr.res.Body) 81 82 go func() { 83 for { 84 // read a line 85 b, err := reader.ReadBytes('\n') 86 if err != nil { 87 return 88 } 89 90 // send the event 91 var event Event 92 if err := json.Unmarshal(b, &event); err != nil { 93 continue 94 } 95 96 select { 97 case <-wr.stop: 98 return 99 case wr.results <- event: 100 } 101 } 102 }() 103 } 104 105 // newWatcher creates a k8s body watcher for 106 // a given http request 107 func newWatcher(req *api.Request) (Watcher, error) { 108 // set request context so we can cancel the request 109 ctx, cancel := context.WithCancel(context.Background()) 110 req.Context(ctx) 111 112 // do the raw request 113 res, err := req.Raw() 114 if err != nil { 115 cancel() 116 return nil, err 117 } 118 119 if res.StatusCode < 200 || res.StatusCode >= 300 { 120 cancel() 121 // close the response body 122 res.Body.Close() 123 // return an error 124 return nil, errors.New(res.Request.URL.String() + ": " + res.Status) 125 } 126 127 wr := &bodyWatcher{ 128 results: make(chan Event), 129 stop: make(chan bool), 130 cancel: cancel, 131 req: req, 132 res: res, 133 } 134 135 go wr.stream() 136 137 return wr, nil 138 }