skywire icon indicating copy to clipboard operation
skywire copied to clipboard

visor flag to watch config for changes & reload on changes

Open 0pcom opened this issue 2 years ago • 0 comments

it should be possible to have a flag for the visor to watch the config for changes and reload, at least selectively, on config changes.

For example, the Caddy server has both a caddy run --watch to watch the config and reload on changes, as well as caddy reload --config to explicitly reload from a (possibly different) config without watching it or automatically reloading.

here is an example implementation using fsnotify

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Start the process in a goroutine
    go runProcess()

    // Set up a signal handler to gracefully exit the program
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    // Set up a watcher to detect changes to the config file
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        fmt.Printf("Error creating watcher: %s\n", err)
        return
    }
    defer watcher.Close()

    // Add the config file to the watcher
    err = watcher.Add("/path/to/config/file")
    if err != nil {
        fmt.Printf("Error adding file to watcher: %s\n", err)
        return
    }

    // Start a loop to watch for changes to the config file
    for {
        select {
        case event, ok := <-watcher.Events:
            if !ok {
                return
            }
            if event.Op&fsnotify.Write == fsnotify.Write {
                fmt.Println("Config file changed, reloading process...")
                // Send a signal to the process to gracefully exit
                err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
                if err != nil {
                    fmt.Printf("Error sending signal to process: %s\n", err)
                }
                // Wait for the process to exit
                time.Sleep(1 * time.Second)
                // Restart the process
                go runProcess()
            }
        case err, ok := <-watcher.Errors:
            if !ok {
                return
            }
            fmt.Printf("Error watching file: %s\n", err)
        case sig := <-sigs:
            fmt.Printf("Received signal %s, exiting...\n", sig)
            // Send a signal to the process to gracefully exit
            err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
            if err != nil {
                fmt.Printf("Error sending signal to process: %s\n", err)
            }
            // Wait for the process to exit
            time.Sleep(1 * time.Second)
            return
        }
    }
}

func runProcess() {
    // Replace this with your actual process code
    for {
        fmt.Println("Running process...")
        time.Sleep(5 * time.Second)
    }
}

Here is an example implementation using spf13/viper

package main

import (
    "fmt"
    "time"

    "github.com/spf13/viper"
)

func main() {
    // Set up Viper to read from a config file
    viper.SetConfigFile("/path/to/config/file")
    if err := viper.ReadInConfig(); err != nil {
        fmt.Printf("Error reading config file: %s\n", err)
        return
    }

    // Watch for changes to the config file
    viper.WatchConfig()
    viper.OnConfigChange(func(e fsnotify.Event) {
        fmt.Println("Config file changed, reloading...")
        // Replace this with your actual code to handle the updated configuration
    })

    // Run your program logic here
    for {
        fmt.Println("Running program...")
        time.Sleep(5 * time.Second)
    }
}

0pcom avatar May 14 '23 14:05 0pcom