9 "github.com/sirupsen/logrus"
10 "github.com/urfave/cli"
11 gcfg "gopkg.in/gcfg.v1"
13 "k8s.io/client-go/kubernetes"
14 "k8s.io/client-go/rest"
15 "k8s.io/client-go/tools/clientcmd"
18 // The following are global config parameters that other modules may access directly
21 // Default holds parsed config file parameters and command-line overrides
22 Default = DefaultConfig{
26 // Logging holds logging-related parsed config file parameters and command-line overrides
27 Logging = LoggingConfig{
28 File: "", // do not log to a file by default
32 // CNI holds CNI-related parsed config file parameters and command-line overrides
34 ConfDir: "/etc/cni/net.d",
35 Plugin: "ovn4nfvk8s-cni",
38 // Kubernetes holds Kubernetes-related parsed config file parameters
39 Kubernetes = KubernetesConfig{}
42 // DefaultConfig holds parsed config file parameters and command-line overrides
43 type DefaultConfig struct {
44 // MTU value used for the overlay networks.
48 // LoggingConfig holds logging-related parsed config file parameters and command-line overrides
49 type LoggingConfig struct {
50 // File is the path of the file to log to
51 File string `gcfg:"logfile"`
52 // Level is the logging verbosity level
53 Level int `gcfg:"loglevel"`
56 // CNIConfig holds CNI-related parsed config file parameters and command-line overrides
57 type CNIConfig struct {
58 // ConfDir specifies the CNI config directory in which to write the overlay CNI config file
59 ConfDir string `gcfg:"conf-dir"`
60 // Plugin specifies the name of the CNI plugin
61 Plugin string `gcfg:"plugin"`
64 // KubernetesConfig holds Kubernetes-related parsed config file parameters and command-line overrides
65 type KubernetesConfig struct {
66 Kubeconfig string `gcfg:"kubeconfig"`
69 // Config is used to read the structured config file and to cache config in testcases
74 Kubernetes KubernetesConfig
77 // copy members of struct 'src' into the corresponding field in struct 'dst'
78 // if the field in 'src' is a non-zero int or a non-zero-length string. This
79 // function should be called with pointers to structs.
80 func overrideFields(dst, src interface{}) {
81 dstStruct := reflect.ValueOf(dst).Elem()
82 srcStruct := reflect.ValueOf(src).Elem()
83 if dstStruct.Kind() != srcStruct.Kind() || dstStruct.Kind() != reflect.Struct {
84 panic("mismatched value types")
86 if dstStruct.NumField() != srcStruct.NumField() {
87 panic("mismatched struct types")
90 for i := 0; i < dstStruct.NumField(); i++ {
91 dstField := dstStruct.Field(i)
92 srcField := srcStruct.Field(i)
93 if dstField.Kind() != srcField.Kind() {
94 panic("mismatched struct fields")
96 switch srcField.Kind() {
98 if srcField.String() != "" {
99 dstField.Set(srcField)
102 if srcField.Int() != 0 {
103 dstField.Set(srcField)
106 panic(fmt.Sprintf("unhandled struct field type: %v", srcField.Kind()))
113 // Flags are general command-line flags. Apps should add these flags to their
114 // own urfave/cli flags and call InitConfig() early in the application.
115 var Flags = []cli.Flag{
118 Usage: "configuration file path (default: /etc/openvswitch/ovn4nfv_k8s.conf)",
124 Usage: "MTU value used for the overlay networks (default: 1400)",
125 Destination: &cliConfig.Default.MTU,
131 Usage: "log verbosity and level: 5=debug, 4=info, 3=warn, 2=error, 1=fatal (default: 4)",
132 Destination: &cliConfig.Logging.Level,
136 Usage: "path of a file to direct log output to",
137 Destination: &cliConfig.Logging.File,
142 Name: "cni-conf-dir",
143 Usage: "the CNI config directory in which to write the overlay CNI config file (default: /etc/cni/net.d)",
144 Destination: &cliConfig.CNI.ConfDir,
148 Usage: "the name of the CNI plugin (default: ovn4nfvk8s-cni)",
149 Destination: &cliConfig.CNI.Plugin,
152 // Kubernetes-related options
154 Name: "k8s-kubeconfig",
155 Usage: "absolute path to the Kubernetes kubeconfig file",
156 Destination: &cliConfig.Kubernetes.Kubeconfig,
160 func buildKubernetesConfig(cli, file *config) error {
162 // Copy config file values over default values
163 overrideFields(&Kubernetes, &file.Kubernetes)
164 // And CLI overrides over config file and default values
165 overrideFields(&Kubernetes, &cli.Kubernetes)
167 if Kubernetes.Kubeconfig == "" || !pathExists(Kubernetes.Kubeconfig) {
168 return fmt.Errorf("kubernetes kubeconfig file %q not found", Kubernetes.Kubeconfig)
173 // getConfigFilePath returns config file path and 'true' if the config file is
174 // the fallback path (eg not given by the user), 'false' if given explicitly
176 func getConfigFilePath(ctx *cli.Context) (string, bool) {
177 configFile := ctx.String("config-file")
178 if configFile != "" {
179 return configFile, false
183 return filepath.Join("/etc", "openvswitch", "ovn4nfv_k8s.conf"), true
187 // InitConfig reads the config file and common command-line options and
188 // constructs the global config object from them. It returns the config file
189 // path (if explicitly specified) or an error
190 func InitConfig(ctx *cli.Context) (string, error) {
191 return InitConfigWithPath(ctx, "")
194 // InitConfigWithPath reads the given config file (or if empty, reads the config file
195 // specified by command-line arguments, or empty, the default config file) and
196 // common command-line options and constructs the global config object from
197 // them. It returns the config file path (if explicitly specified) or an error
198 func InitConfigWithPath(ctx *cli.Context, configFile string) (string, error) {
200 var retConfigFile string
201 var configFileIsDefault bool
203 // If no specific config file was given, try to find one from command-line
204 // arguments, or the platform-specific default config file path
205 if configFile == "" {
206 configFile, configFileIsDefault = getConfigFilePath(ctx)
209 logrus.SetOutput(os.Stderr)
211 if !configFileIsDefault {
212 // Only return explicitly specified config file
213 retConfigFile = configFile
216 f, err := os.Open(configFile)
217 // Failure to find a default config file is not a hard error
218 if err != nil && !configFileIsDefault {
219 return "", fmt.Errorf("failed to open config file %s: %v", configFile, err)
224 // Parse ovn4nfvk8s config file.
225 if err = gcfg.ReadInto(&cfg, f); err != nil {
226 return "", fmt.Errorf("failed to parse config file %s: %v", f.Name(), err)
228 logrus.Infof("Parsed config file %s", f.Name())
229 logrus.Infof("Parsed config: %+v", cfg)
232 // Build config that needs no special processing
233 overrideFields(&Default, &cfg.Default)
234 overrideFields(&Default, &cliConfig.Default)
235 overrideFields(&CNI, &cfg.CNI)
236 overrideFields(&CNI, &cliConfig.CNI)
239 overrideFields(&Logging, &cfg.Logging)
240 overrideFields(&Logging, &cliConfig.Logging)
241 logrus.SetLevel(logrus.Level(Logging.Level))
242 if Logging.File != "" {
244 file, err = os.OpenFile(Logging.File, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0660)
246 logrus.Errorf("failed to open logfile %s (%v). Ignoring..", Logging.File, err)
248 logrus.SetOutput(file)
252 if err = buildKubernetesConfig(&cliConfig, &cfg); err != nil {
255 logrus.Debugf("Default config: %+v", Default)
256 logrus.Debugf("Logging config: %+v", Logging)
257 logrus.Debugf("CNI config: %+v", CNI)
258 logrus.Debugf("Kubernetes config: %+v", Kubernetes)
260 return retConfigFile, nil
263 func pathExists(path string) bool {
264 _, err := os.Stat(path)
265 if err != nil && os.IsNotExist(err) {
271 // NewClientset creates a Kubernetes clientset
272 func NewClientset(conf *KubernetesConfig) (*kubernetes.Clientset, error) {
273 var kconfig *rest.Config
276 if conf.Kubeconfig != "" {
277 // uses the current context in kubeconfig
278 kconfig, err = clientcmd.BuildConfigFromFlags("", conf.Kubeconfig)
284 return kubernetes.NewForConfig(kconfig)