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  }