github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/watch.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	"gopkg.in/tomb.v2"
    13  
    14  	apiservererrors "github.com/juju/juju/apiserver/errors"
    15  	"github.com/juju/juju/apiserver/facade"
    16  	"github.com/juju/juju/rpc/params"
    17  	"github.com/juju/juju/state"
    18  	"github.com/juju/juju/state/watcher"
    19  )
    20  
    21  // AgentEntityWatcher implements a common Watch method for use by
    22  // various facades.
    23  type AgentEntityWatcher struct {
    24  	st          state.EntityFinder
    25  	resources   facade.Resources
    26  	getCanWatch GetAuthFunc
    27  }
    28  
    29  // NewAgentEntityWatcher returns a new AgentEntityWatcher. The
    30  // GetAuthFunc will be used on each invocation of Watch to determine
    31  // current permissions.
    32  func NewAgentEntityWatcher(st state.EntityFinder, resources facade.Resources, getCanWatch GetAuthFunc) *AgentEntityWatcher {
    33  	return &AgentEntityWatcher{
    34  		st:          st,
    35  		resources:   resources,
    36  		getCanWatch: getCanWatch,
    37  	}
    38  }
    39  
    40  func (a *AgentEntityWatcher) watchEntity(tag names.Tag) (string, error) {
    41  	entity0, err := a.st.FindEntity(tag)
    42  	if err != nil {
    43  		return "", err
    44  	}
    45  	entity, ok := entity0.(state.NotifyWatcherFactory)
    46  	if !ok {
    47  		return "", apiservererrors.NotSupportedError(tag, "watching")
    48  	}
    49  	watch := entity.Watch()
    50  	// Consume the initial event. Technically, API
    51  	// calls to Watch 'transmit' the initial event
    52  	// in the Watch response. But NotifyWatchers
    53  	// have no state to transmit.
    54  	if _, ok := <-watch.Changes(); ok {
    55  		return a.resources.Register(watch), nil
    56  	}
    57  	return "", watcher.EnsureErr(watch)
    58  }
    59  
    60  // Watch starts an NotifyWatcher for each given entity.
    61  func (a *AgentEntityWatcher) Watch(args params.Entities) (params.NotifyWatchResults, error) {
    62  	result := params.NotifyWatchResults{
    63  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
    64  	}
    65  	if len(args.Entities) == 0 {
    66  		return result, nil
    67  	}
    68  	canWatch, err := a.getCanWatch()
    69  	if err != nil {
    70  		return params.NotifyWatchResults{}, errors.Trace(err)
    71  	}
    72  	for i, entity := range args.Entities {
    73  		tag, err := names.ParseTag(entity.Tag)
    74  		if err != nil {
    75  			result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm)
    76  			continue
    77  		}
    78  		err = apiservererrors.ErrPerm
    79  		watcherId := ""
    80  		if canWatch(tag) {
    81  			watcherId, err = a.watchEntity(tag)
    82  		}
    83  		result.Results[i].NotifyWatcherId = watcherId
    84  		result.Results[i].Error = apiservererrors.ServerError(err)
    85  	}
    86  	return result, nil
    87  }
    88  
    89  // MultiNotifyWatcher implements state.NotifyWatcher, combining
    90  // multiple NotifyWatchers.
    91  type MultiNotifyWatcher struct {
    92  	tomb     tomb.Tomb
    93  	watchers []state.NotifyWatcher
    94  	changes  chan struct{}
    95  }
    96  
    97  // NewMultiNotifyWatcher creates a NotifyWatcher that combines
    98  // each of the NotifyWatchers passed in. Each watcher's initial
    99  // event is consumed, and a single initial event is sent.
   100  // Subsequent events are not coalesced.
   101  func NewMultiNotifyWatcher(w ...state.NotifyWatcher) *MultiNotifyWatcher {
   102  	m := &MultiNotifyWatcher{
   103  		watchers: w,
   104  		changes:  make(chan struct{}),
   105  	}
   106  	var wg sync.WaitGroup
   107  	wg.Add(len(w))
   108  	staging := make(chan struct{})
   109  	for _, w := range w {
   110  		// Consume the first event of each watcher.
   111  		<-w.Changes()
   112  		go func(wCopy state.NotifyWatcher) {
   113  			defer wg.Done()
   114  			_ = wCopy.Wait()
   115  		}(w)
   116  		// Copy events from the watcher to the staging channel.
   117  		go copyEvents(staging, w.Changes(), &m.tomb)
   118  	}
   119  	m.tomb.Go(func() error {
   120  		m.loop(staging)
   121  		wg.Wait()
   122  		return nil
   123  	})
   124  	return m
   125  }
   126  
   127  // loop copies events from the input channel to the output channel,
   128  // coalescing events by waiting a short time between receiving and
   129  // sending.
   130  func (w *MultiNotifyWatcher) loop(in <-chan struct{}) {
   131  	defer close(w.changes)
   132  	// out is initialised to m.changes to send the initial event.
   133  	out := w.changes
   134  	var timer <-chan time.Time
   135  	for {
   136  		select {
   137  		case <-w.tomb.Dying():
   138  			return
   139  		case <-in:
   140  			if timer == nil {
   141  				// TODO(fwereade): 2016-03-17 lp:1558657
   142  				timer = time.After(10 * time.Millisecond)
   143  			}
   144  		case <-timer:
   145  			timer = nil
   146  			out = w.changes
   147  		case out <- struct{}{}:
   148  			out = nil
   149  		}
   150  	}
   151  }
   152  
   153  // copyEvents copies channel events from "in" to "out", coalescing.
   154  func copyEvents(out chan<- struct{}, in <-chan struct{}, tomb *tomb.Tomb) {
   155  	var outC chan<- struct{}
   156  	for {
   157  		select {
   158  		case <-tomb.Dying():
   159  			return
   160  		case _, ok := <-in:
   161  			if !ok {
   162  				return
   163  			}
   164  			outC = out
   165  		case outC <- struct{}{}:
   166  			outC = nil
   167  		}
   168  	}
   169  }
   170  
   171  func (w *MultiNotifyWatcher) Kill() {
   172  	w.tomb.Kill(nil)
   173  	for _, w := range w.watchers {
   174  		w.Kill()
   175  	}
   176  }
   177  
   178  func (w *MultiNotifyWatcher) Wait() error {
   179  	return w.tomb.Wait()
   180  }
   181  
   182  func (w *MultiNotifyWatcher) Stop() error {
   183  	w.Kill()
   184  	return w.Wait()
   185  }
   186  
   187  func (w *MultiNotifyWatcher) Err() error {
   188  	return w.tomb.Err()
   189  }
   190  
   191  func (w *MultiNotifyWatcher) Changes() <-chan struct{} {
   192  	return w.changes
   193  }