github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/caas/kubernetes/provider/k8swatcher.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provider
     5  
     6  import (
     7  	"time"
     8  
     9  	jujuclock "github.com/juju/clock"
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/worker.v1/catacomb"
    12  	core "k8s.io/api/core/v1"
    13  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    14  	"k8s.io/apimachinery/pkg/watch"
    15  
    16  	"github.com/juju/juju/core/watcher"
    17  )
    18  
    19  // kubernetesWatcher reports changes to kubernetes
    20  // resources. A native kubernetes watcher is passed
    21  // in to generate change events from the kubernetes
    22  // model. These events are consolidated into a Juju
    23  // notification watcher event.
    24  type kubernetesWatcher struct {
    25  	clock    jujuclock.Clock
    26  	catacomb catacomb.Catacomb
    27  
    28  	out       chan struct{}
    29  	name      string
    30  	k8watcher watch.Interface
    31  }
    32  
    33  func newKubernetesWatcher(wi watch.Interface, name string, clock jujuclock.Clock) (*kubernetesWatcher, error) {
    34  	w := &kubernetesWatcher{
    35  		clock:     clock,
    36  		out:       make(chan struct{}),
    37  		name:      name,
    38  		k8watcher: wi,
    39  	}
    40  	err := catacomb.Invoke(catacomb.Plan{
    41  		Site: &w.catacomb,
    42  		Work: w.loop,
    43  	})
    44  	return w, err
    45  }
    46  
    47  const sendDelay = 1 * time.Second
    48  
    49  func (w *kubernetesWatcher) loop() error {
    50  	defer close(w.out)
    51  	defer w.k8watcher.Stop()
    52  
    53  	var out chan struct{}
    54  	// Set delayCh now so that initial event is sent.
    55  	delayCh := w.clock.After(sendDelay)
    56  	for {
    57  		select {
    58  		case <-w.catacomb.Dying():
    59  			return w.catacomb.ErrDying()
    60  		case evt, ok := <-w.k8watcher.ResultChan():
    61  			// This can happen if the k8s API connection drops.
    62  			if !ok {
    63  				return errors.Errorf("k8s event watcher closed, restarting")
    64  			}
    65  			logger.Tracef("received k8s event: %+v", evt.Type)
    66  			if pod, ok := evt.Object.(*core.Pod); ok {
    67  				logger.Tracef("%v(%v) = %v, status=%+v", pod.Name, pod.UID, pod.Labels, pod.Status)
    68  			}
    69  			if ns, ok := evt.Object.(*core.Namespace); ok {
    70  				logger.Tracef("%v(%v) = %v, status=%+v", ns.Name, ns.UID, ns.Labels, ns.Status)
    71  			}
    72  			if evt.Type == watch.Error {
    73  				return errors.Errorf("kubernetes watcher error: %v", k8serrors.FromObject(evt.Object))
    74  			}
    75  			if delayCh == nil {
    76  				delayCh = w.clock.After(sendDelay)
    77  			}
    78  		case <-delayCh:
    79  			out = w.out
    80  		case out <- struct{}{}:
    81  			logger.Debugf("fire notify watcher for %v", w.name)
    82  			out = nil
    83  			delayCh = nil
    84  		}
    85  	}
    86  }
    87  
    88  // Changes returns the event channel for this watcher.
    89  func (w *kubernetesWatcher) Changes() watcher.NotifyChannel {
    90  	return w.out
    91  }
    92  
    93  // Kill asks the watcher to stop without waiting for it do so.
    94  func (w *kubernetesWatcher) Kill() {
    95  	w.catacomb.Kill(nil)
    96  }
    97  
    98  // Wait waits for the watcher to die and returns any
    99  // error encountered when it was running.
   100  func (w *kubernetesWatcher) Wait() error {
   101  	return w.catacomb.Wait()
   102  }