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  }