package cmd import ( "fmt" "os" "strings" "github.com/spf13/cobra" "github.com/spf13/viper" "go.infratographer.com/x/loggingx" "go.infratographer.com/x/otelx" "go.infratographer.com/x/versionx" "go.infratographer.com/x/viperx" "go.uber.org/zap" "go.equinixmetal.net/infra9-metal-bridge/internal/config" ) const appName = "infra9-metal-bridge" var ( cfgFile string logger *zap.SugaredLogger ) // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "infra9-metal-bridge", Short: "Bridges Metal to Infratographer services", } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { err := rootCmd.Execute() if err != nil { os.Exit(1) } } func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is /etc/infratographer/infra9-metal-bridge.yaml)") viperx.MustBindFlag(viper.GetViper(), "config", rootCmd.PersistentFlags().Lookup("config")) // Logging flags loggingx.MustViperFlags(viper.GetViper(), rootCmd.PersistentFlags()) // Register version command versionx.RegisterCobraCommand(rootCmd, func() { versionx.PrintVersion(logger) }) // OTEL Flags otelx.MustViperFlags(viper.GetViper(), rootCmd.Flags()) } // initConfig reads in config file and ENV variables if set. func initConfig() { if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) } else { viper.AddConfigPath("/etc/infratographer/") viper.SetConfigType("yaml") viper.SetConfigName("infra9-metal-bridge") } viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) viper.SetEnvPrefix("infra9metalbridge") viper.AutomaticEnv() // read in environment variables that match setupAppConfig() // setupLogging() logger = loggingx.InitLogger(appName, config.AppConfig.Logging) // If a config file is found, read it in. err := viper.ReadInConfig() if err == nil { logger.Infow("using config file", "file", viper.ConfigFileUsed(), ) } setupAppConfig() } // setupAppConfig loads our config.AppConfig struct with the values bound by // viper. Then, anywhere we need these values, we can just return to AppConfig // instead of performing viper.GetString(...), viper.GetBool(...), etc. func setupAppConfig() { err := viper.Unmarshal(&config.AppConfig) if err != nil { fmt.Printf("unable to decode app config: %s", err) os.Exit(1) } }