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 }