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  }