1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
package cmd
import (
"fmt"
"os"
"reflect"
receptorVersion "github.com/ansible/receptor/internal/version"
"github.com/ansible/receptor/pkg/logger"
"github.com/ansible/receptor/pkg/netceptor"
"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
cfgFile string
version bool
backendConfig *BackendConfig
)
// rootCmd represents the base command when called without any subcommands.
var rootCmd = &cobra.Command{
Use: "receptor",
Short: "Run a receptor instance.",
Long: `
Receptor is an overlay network intended to ease the distribution of work across a large and dispersed collection of workers.
Receptor nodes establish peer-to-peer connections with each other via existing networks.
Once connected, the receptor mesh provides datagram (UDP-like) and stream (TCP-like) capabilities to applications, as well as robust unit-of-work handling with resiliency against transient network failures.`,
Run: handleRootCommand,
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.Flags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/receptor.yaml)")
rootCmd.Flags().BoolVar(&version, "version", false, "Show the Receptor version")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
l := logger.NewReceptorLogger("")
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, err := os.UserHomeDir()
cobra.CheckErr(err)
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName("receptor")
}
viper.AutomaticEnv()
viper.OnConfigChange(func(e fsnotify.Event) {
l.Info("Config file changed: %s\n", e.Name)
var newConfig *BackendConfig
viper.Unmarshal(&newConfig)
// used because OnConfigChange runs twice for some reason
// allows to skip empty first config
isEmpty := isConfigEmpty(reflect.ValueOf(*newConfig))
if isEmpty {
return
}
SetBackendConfigDefaults(newConfig)
isEqual := reflect.DeepEqual(*backendConfig, *newConfig)
if !isEqual {
// fmt.Println("reloading backends")
// this will do a reload of all reloadable services
// TODO: Optimize to only reload services that have config change
// NOTE: Make sure to account for two things
// if current config had two services then new config has zero cancel those backends
// if services has two items in a slice and one of them has changed iterate and reload on changed service
netceptor.MainInstance.CancelBackends()
l.Info("Reloading backends")
ReloadServices(reflect.ValueOf(*newConfig))
backendConfig = newConfig
return
}
l.Info("No reloadable backends were found.")
})
// TODO: use env to turn off watch config
viper.WatchConfig()
err := viper.ReadInConfig()
if err == nil {
fmt.Fprintln(os.Stdout, "Using config file:", viper.ConfigFileUsed())
}
}
func handleRootCommand(cmd *cobra.Command, args []string) {
if version {
fmt.Println(receptorVersion.Version)
os.Exit(0)
}
if cfgFile == "" && viper.ConfigFileUsed() == "" {
fmt.Fprintln(os.Stderr, "Could not locate config file (default is $HOME/receptor.yaml)")
os.Exit(1)
}
receptorConfig, err := ParseReceptorConfig(cfgFile)
if err != nil {
fmt.Printf("unable to decode into struct, %v", err)
os.Exit(1)
}
certifcatesConfig, err := ParseCertificatesConfig(cfgFile)
if err != nil {
fmt.Printf("unable to decode into struct, %v", err)
os.Exit(1)
}
backendConfig, err = ParseBackendConfig(cfgFile)
if err != nil {
fmt.Printf("unable to decode into struct, %v", err)
os.Exit(1)
}
isEmptyReceptorConfig := isConfigEmpty(reflect.ValueOf(*receptorConfig))
isEmptyReloadableServicesConfig := isConfigEmpty(reflect.ValueOf(*backendConfig))
RunConfigV2(reflect.ValueOf(*certifcatesConfig))
if isEmptyReceptorConfig && isEmptyReloadableServicesConfig {
fmt.Println("empty receptor config, skipping...")
os.Exit(0)
}
SetReceptorConfigDefaults(receptorConfig)
SetBackendConfigDefaults(backendConfig)
RunConfigV2(reflect.ValueOf(*receptorConfig))
RunConfigV2(reflect.ValueOf(*backendConfig))
}
|