File: root.go

package info (click to toggle)
receptor 1.5.5-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,772 kB
  • sloc: python: 1,643; makefile: 305; sh: 174
file content (149 lines) | stat: -rw-r--r-- 4,161 bytes parent folder | download | duplicates (2)
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))
}