github.com/kamilsk/grafaman@v1.0.0-beta3.0.20201207211242-3e0d02dd84ce/internal/cnf/option.go (about) 1 package cnf 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "time" 8 9 "github.com/sirupsen/logrus" 10 "github.com/spf13/cobra" 11 "github.com/spf13/viper" 12 "go.octolab.org/fn" 13 xcobra "go.octolab.org/toolkit/cli/cobra" 14 "go.octolab.org/toolkit/cli/debugger" 15 "go.octolab.org/unsafe" 16 17 "github.com/kamilsk/grafaman/internal/presenter" 18 ) 19 20 // Apply is an alias for the toolkit method. 21 var Apply = xcobra.Apply 22 23 // WithConfig returns an Option to inject configuration from a container and config files into the Config. 24 func WithConfig(config *Config) xcobra.Option { 25 return func(command *cobra.Command, container *viper.Viper) { 26 xcobra.BeforeE(&command.PreRunE, func(cmd *cobra.Command, args []string) error { 27 cfg := viper.New() 28 cfg.SetConfigFile(config.File) 29 cfg.SetConfigType("dotenv") 30 switch err := cfg.ReadInConfig(); { 31 case err == nil: 32 fn.Must(func() error { return container.MergeConfigMap(cfg.AllSettings()) }) 33 case os.IsNotExist(err): 34 cfg.SetConfigFile("app.toml") 35 cfg.SetConfigType("toml") 36 if err, sub := cfg.ReadInConfig(), cfg.Sub("envs.local.env_vars"); err == nil && sub != nil { 37 fn.Must( 38 func() error { 39 return container.MergeConfigMap(map[string]interface{}{"app_name": cfg.GetString("name")}) 40 }, 41 func() error { return container.MergeConfigMap(sub.AllSettings()) }, 42 ) 43 } 44 } 45 46 fn.Must(func() error { return container.Unmarshal(config) }) 47 48 // ad hoc 49 if config.Graphite.Prefix == "" && config.App != "" { 50 config.Graphite.Prefix = fmt.Sprintf("apps.services.%s", config.App) 51 } 52 53 return nil 54 }) 55 } 56 } 57 58 // WithDebug returns an Option to inject debugger and configure the logger. 59 func WithDebug(config *Config, logger *logrus.Logger) xcobra.Option { 60 return func(command *cobra.Command, container *viper.Viper) { 61 flags := command.Flags() 62 flags.Bool("debug", false, "enable debug") 63 flags.String("debug-host", "localhost:", "specific debug host") 64 flags.CountP("verbose", "v", "increase the verbosity of messages if debug enabled") 65 66 fn.Must( 67 func() error { return container.BindPFlag("debug.enabled", flags.Lookup("debug")) }, 68 func() error { return container.BindPFlag("debug.host", flags.Lookup("debug-host")) }, 69 func() error { return container.BindPFlag("debug.level", flags.Lookup("verbose")) }, 70 ) 71 72 xcobra.BeforeE(&command.PreRunE, func(cmd *cobra.Command, args []string) error { 73 logger.SetOutput(ioutil.Discard) 74 if config.Debug.Enabled { 75 logger.SetOutput(cmd.ErrOrStderr()) 76 switch verbose := config.Debug.Level; { 77 case verbose == 1: 78 logger.SetLevel(logrus.WarnLevel) 79 case verbose == 2: 80 logger.SetLevel(logrus.InfoLevel) 81 case verbose > 2: 82 logger.SetLevel(logrus.DebugLevel) 83 default: 84 logger.SetLevel(logrus.ErrorLevel) 85 } 86 87 d, err := debugger.New(debugger.WithSpecificHost(config.Debug.Host)) 88 if err != nil { 89 return err 90 } 91 host, _ := d.Debug(unsafe.Ignore) 92 logger.Warningf("start listen and serve pprof at http://%s/debug/pprof/", host) 93 } 94 95 return nil 96 }) 97 } 98 } 99 100 // WithGrafana returns an Option to inject flags related to Grafana configuration. 101 func WithGrafana() xcobra.Option { 102 return func(command *cobra.Command, container *viper.Viper) { 103 flags := command.Flags() 104 flags.String("grafana", "", "Grafana API endpoint") 105 flags.StringP("dashboard", "d", "", "a dashboard unique identifier") 106 flags.Duration("grafana-timeout", time.Second, "timeout duration for Grafana API requests") 107 108 container.RegisterAlias("grafana", "grafana_url") 109 container.RegisterAlias("dashboard", "grafana_dashboard") 110 111 fn.Must( 112 func() error { return container.BindEnv("grafana_url", "GRAFANA_URL") }, 113 func() error { return container.BindPFlag("grafana_url", flags.Lookup("grafana")) }, 114 func() error { return container.BindEnv("grafana_dashboard", "GRAFANA_DASHBOARD") }, 115 func() error { return container.BindPFlag("grafana_dashboard", flags.Lookup("dashboard")) }, 116 func() error { return container.BindEnv("grafana_timeout", "GRAFANA_TIMEOUT") }, 117 func() error { return container.BindPFlag("grafana_timeout", flags.Lookup("grafana-timeout")) }, 118 ) 119 } 120 } 121 122 // WithGraphite returns an Option to inject flags related to Graphite configuration. 123 func WithGraphite() xcobra.Option { 124 return func(command *cobra.Command, container *viper.Viper) { 125 flags := command.Flags() 126 flags.String("filter", "", "query to filter metrics, e.g. some.*.metric") 127 flags.String("graphite", "", "Graphite API endpoint") 128 flags.Duration("graphite-timeout", time.Second, "timeout duration for Graphite API requests") 129 130 container.RegisterAlias("graphite", "graphite_url") 131 132 fn.Must( 133 func() error { return container.BindPFlag("filter", flags.Lookup("filter")) }, 134 func() error { return container.BindEnv("graphite_url", "GRAPHITE_URL") }, 135 func() error { return container.BindPFlag("graphite_url", flags.Lookup("graphite")) }, 136 func() error { return container.BindEnv("graphite_timeout", "GRAPHITE_TIMEOUT") }, 137 func() error { return container.BindPFlag("graphite_timeout", flags.Lookup("graphite-timeout")) }, 138 ) 139 140 WithGraphiteMetrics()(command, container) 141 } 142 } 143 144 // WithGraphiteMetrics returns an Option to inject flags related to Graphite configuration. 145 func WithGraphiteMetrics() xcobra.Option { 146 return func(command *cobra.Command, container *viper.Viper) { 147 flags := command.Flags() 148 flags.StringP("metrics", "m", "", "the required subset of metrics (must be a simple prefix)") 149 150 container.RegisterAlias("app", "app_name") 151 container.RegisterAlias("metrics", "graphite_metrics") 152 153 fn.Must( 154 func() error { return container.BindEnv("app_name", "APP_NAME") }, 155 func() error { return container.BindEnv("graphite_metrics", "GRAPHITE_METRICS") }, 156 func() error { return container.BindPFlag("graphite_metrics", flags.Lookup("metrics")) }, 157 ) 158 } 159 } 160 161 // WithOutputFormat returns an Option to inject flags related to output format. 162 func WithOutputFormat() xcobra.Option { 163 return func(command *cobra.Command, container *viper.Viper) { 164 flags := command.Flags() 165 flags.StringP("format", "f", presenter.DefaultFormat, "output format") 166 167 fn.Must(func() error { return container.BindPFlag("output.format", flags.Lookup("format")) }) 168 } 169 }