github.com/argoproj/argo-cd/v3@v3.2.1/reposerver/gpgwatcher.go (about)

     1  package reposerver
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"path"
     7  	"time"
     8  
     9  	"github.com/fsnotify/fsnotify"
    10  	log "github.com/sirupsen/logrus"
    11  
    12  	"github.com/argoproj/argo-cd/v3/util/gpg"
    13  )
    14  
    15  const maxRecreateRetries = 5
    16  
    17  // StartGPGWatcher watches a given directory for creation and deletion of files and syncs the GPG keyring
    18  func StartGPGWatcher(sourcePath string) error {
    19  	log.Infof("Starting GPG sync watcher on directory '%s'", sourcePath)
    20  	forceSync := false
    21  	watcher, err := fsnotify.NewWatcher()
    22  	if err != nil {
    23  		return fmt.Errorf("failed to create fsnotify Watcher: %w", err)
    24  	}
    25  	defer func(watcher *fsnotify.Watcher) {
    26  		if err = watcher.Close(); err != nil {
    27  			log.Errorf("Error closing watcher: %v", err)
    28  		}
    29  	}(watcher)
    30  
    31  	done := make(chan bool)
    32  	go func() {
    33  		for {
    34  			select {
    35  			case event, ok := <-watcher.Events:
    36  				if !ok {
    37  					return
    38  				}
    39  				if event.Has(fsnotify.Create) || event.Has(fsnotify.Remove) {
    40  					// In case our watched path is re-created (i.e. during e2e tests), we need to watch again
    41  					// For more robustness, we retry re-creating the watcher up to maxRecreateRetries
    42  					if event.Name == sourcePath && event.Has(fsnotify.Remove) {
    43  						log.Warnf("Re-creating watcher on %s", sourcePath)
    44  						attempt := 0
    45  						for {
    46  							err = watcher.Add(sourcePath)
    47  							if err != nil {
    48  								log.Errorf("Error re-creating watcher on %s: %v", sourcePath, err)
    49  								if attempt < maxRecreateRetries {
    50  									attempt++
    51  									log.Infof("Retrying to re-create watcher, attempt %d of %d", attempt, maxRecreateRetries)
    52  									time.Sleep(1 * time.Second)
    53  									continue
    54  								}
    55  								log.Errorf("Maximum retries exceeded.")
    56  								close(done)
    57  								return
    58  							}
    59  							break
    60  						}
    61  						// Force sync because we probably missed an event
    62  						forceSync = true
    63  					}
    64  					if gpg.IsShortKeyID(path.Base(event.Name)) || forceSync {
    65  						log.Infof("Updating GPG keyring on filesystem event")
    66  						added, removed, err := gpg.SyncKeyRingFromDirectory(sourcePath)
    67  						if err != nil {
    68  							log.Errorf("Could not sync keyring: %s", err.Error())
    69  						} else {
    70  							log.Infof("Result of sync operation: keys added: %d, keys removed: %d", len(added), len(removed))
    71  						}
    72  						forceSync = false
    73  					}
    74  				}
    75  			case err, ok := <-watcher.Errors:
    76  				if !ok {
    77  					return
    78  				}
    79  				log.Errorf("%v", err)
    80  			}
    81  		}
    82  	}()
    83  
    84  	err = watcher.Add(sourcePath)
    85  	if err != nil {
    86  		return fmt.Errorf("failed to add a new source to the watcher: %w", err)
    87  	}
    88  	<-done
    89  	return errors.New("abnormal termination of GPG watcher, refusing to continue")
    90  }