github.com/mutagen-io/mutagen@v0.18.0-rc1/cmd/mutagen-agent/synchronizer.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "os/signal" 8 "time" 9 10 "github.com/spf13/cobra" 11 12 "github.com/mutagen-io/mutagen/cmd" 13 14 "github.com/mutagen-io/mutagen/pkg/agent" 15 "github.com/mutagen-io/mutagen/pkg/housekeeping" 16 "github.com/mutagen-io/mutagen/pkg/logging" 17 "github.com/mutagen-io/mutagen/pkg/mutagen" 18 "github.com/mutagen-io/mutagen/pkg/synchronization/endpoint/remote" 19 ) 20 21 const ( 22 // housekeepingInterval is the interval at which housekeeping will be 23 // invoked by the agent. 24 housekeepingInterval = 24 * time.Hour 25 ) 26 27 // housekeepRegularly is the entry point for the housekeeping Goroutine. 28 func housekeepRegularly(ctx context.Context, logger *logging.Logger) { 29 // Perform an initial housekeeping operation since the ticker won't fire 30 // straight away. 31 logger.Info("Performing initial housekeeping") 32 housekeeping.Housekeep() 33 34 // Create a ticker to regulate housekeeping and defer its shutdown. 35 ticker := time.NewTicker(housekeepingInterval) 36 defer ticker.Stop() 37 38 // Loop and wait for the ticker or cancellation. 39 for { 40 select { 41 case <-ctx.Done(): 42 return 43 case <-ticker.C: 44 logger.Info("Performing regular housekeeping") 45 housekeeping.Housekeep() 46 } 47 } 48 } 49 50 // synchronizerMain is the entry point for the synchronizer command. 51 func synchronizerMain(_ *cobra.Command, _ []string) error { 52 // Create a channel to track termination signals. We do this before creating 53 // and starting other infrastructure so that we can ensure things terminate 54 // smoothly, not mid-initialization. 55 signalTermination := make(chan os.Signal, 1) 56 signal.Notify(signalTermination, cmd.TerminationSignals...) 57 58 // Set up a logger on the standard error stream. 59 logLevel := logging.LevelInfo 60 if synchronizerConfiguration.logLevel != "" { 61 if l, ok := logging.NameToLevel(synchronizerConfiguration.logLevel); !ok { 62 return fmt.Errorf("invalid log level specified: %s", synchronizerConfiguration.logLevel) 63 } else { 64 logLevel = l 65 } 66 } 67 logger := logging.NewLogger(logLevel, os.Stderr) 68 69 // Set up regular housekeeping and defer its shutdown. 70 ctx, cancel := context.WithCancel(context.Background()) 71 defer cancel() 72 go housekeepRegularly(ctx, logger.Sublogger("housekeeping")) 73 74 // Create a stream using standard input/output. 75 stream := newStdioStream() 76 77 // Perform an agent handshake. 78 if err := agent.ServerHandshake(stream); err != nil { 79 return fmt.Errorf("server handshake failed: %w", err) 80 } 81 82 // Perform a version handshake. 83 if err := mutagen.ServerVersionHandshake(stream); err != nil { 84 return fmt.Errorf("version handshake error: %w", err) 85 } 86 87 // Serve a synchronizer on standard input/output and monitor for its 88 // termination. 89 synchronizationTermination := make(chan error, 1) 90 go func() { 91 synchronizationTermination <- remote.ServeEndpoint(logger, stream) 92 }() 93 94 // Wait for termination from a signal or the synchronizer. 95 select { 96 case s := <-signalTermination: 97 return fmt.Errorf("terminated by signal: %s", s) 98 case err := <-synchronizationTermination: 99 return fmt.Errorf("synchronization terminated: %w", err) 100 } 101 } 102 103 // synchronizerCommand is the synchronizer command. 104 var synchronizerCommand = &cobra.Command{ 105 Use: agent.CommandSynchronizer, 106 Short: "Run the agent in synchronizer mode", 107 Args: cmd.DisallowArguments, 108 RunE: synchronizerMain, 109 SilenceUsage: true, 110 } 111 112 // synchronizerConfiguration stores configuration for the synchronizer command. 113 var synchronizerConfiguration struct { 114 // help indicates whether or not to show help information and exit. 115 help bool 116 // logLevel indicates the log level to use. 117 logLevel string 118 } 119 120 func init() { 121 // Grab a handle for the command line flags. 122 flags := synchronizerCommand.Flags() 123 124 // Disable alphabetical sorting of flags in help output. 125 flags.SortFlags = false 126 127 // Manually add a help flag to override the default message. Cobra will 128 // still implement its logic automatically. 129 flags.BoolVarP(&synchronizerConfiguration.help, "help", "h", false, "Show help information") 130 131 // Wire up logging flags. 132 flags.StringVar(&synchronizerConfiguration.logLevel, agent.FlagLogLevel, "", "Set the log level") 133 }