func watchCerts(ctx context.Context, certsDirs []string, watchFileEventsFn watchFileEventsFn,
	minDelay time.Duration, updateFunc func()) {
	fw, err := fsnotify.NewWatcher()
	if err != nil {
		glog.Warningf("failed to create a watcher for certificate files: %v", err)
		return
	}
	defer func() {
		if err := fw.Close(); err != nil {
			glog.Warningf("closing watcher encounters an error %v", err)
		}
	}()
	for _, d := range certsDirs {
		if err := fw.Watch(d); err != nil {
			glog.Warningf("watching %s encounters an error %v", d, err)
			return
		}
	}
	watchFileEventsFn(ctx, fw.Event, minDelay, updateFunc)
}
watchCerts watches all certificate directories and calls the provided
`updateFunc` method when changes are detected. This method is blocking
so it should be run as a goroutine.
updateFunc will not be called more than one time per minDelay.
package envoy
import (
	"context"
	"crypto/sha256"
	"fmt"
	"hash"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"time"
	"github.com/golang/glog"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/howeyc/fsnotify"
	proxyconfig "istio.io/api/proxy/v1/config"
	"istio.io/pilot/proxy"
)
type Watcher interface {
	Run(context.Context)
	Reload()
}
type CertSource struct {
	Directory string
	Files []string
}
type watcher struct {
	agent    proxy.Agent
	role     proxy.Node
	config   proxyconfig.ProxyConfig
	certs    []CertSource
	pilotSAN []string
}
func NewWatcher(config proxyconfig.ProxyConfig, agent proxy.Agent, role proxy.Node,
	certs []CertSource, pilotSAN []string) Watcher {
	return &watcher{
		agent:    agent,
		role:     role,
		config:   config,
		certs:    certs,
		pilotSAN: pilotSAN,
	}
}
const defaultMinDelay = 10 * time.Second
func (w *watcher) Run(ctx context.Context) {
	go w.agent.Run(ctx)
	w.Reload()
	certDirs := make([]string, 0, len(w.certs))
	for _, cert := range w.certs {
		certDirs = append(certDirs, cert.Directory)
	}
	go watchCerts(ctx, certDirs, watchFileEvents, defaultMinDelay, w.Reload)
	<-ctx.Done()
}
func (w *watcher) Reload() {
	config := buildConfig(w.config, w.pilotSAN)
	h := sha256.New()
	for _, cert := range w.certs {
		generateCertHash(h, cert.Directory, cert.Files)
	}
	config.Hash = h.Sum(nil)
	w.agent.ScheduleConfigUpdate(config)
}
type watchFileEventsFn func(ctx context.Context, wch <-chan *fsnotify.FileEvent,
	minDelay time.Duration, notifyFn func())
func watchFileEvents(ctx context.Context, wch <-chan *fsnotify.FileEvent, minDelay time.Duration, notifyFn func()) {
	var timeChan <-chan time.Time
	var timer *time.Timer
	for {
		select {
		case ev := <-wch:
			glog.Infof("watchFileEvents: %s", ev.String())
			if timer != nil {
				continue
			}
			timer = time.NewTimer(minDelay)
			timeChan = timer.C
		case <-timeChan:
			timeChan = nil
			timer.Stop()
			timer = nil
			glog.Info("watchFileEvents: notifying")
			notifyFn()
		case <-ctx.Done():
			glog.V(2).Info("watchFileEvents has terminated")
			return
		}
	}
}