gitee.com/sasukebo/go-micro/v4@v4.7.1/cmd/cmd.go (about)

     1  // Package cmd is an interface for parsing the command line
     2  package cmd
     3  
     4  import (
     5  	"fmt"
     6  	"math/rand"
     7  	"strings"
     8  	"time"
     9  
    10  	"gitee.com/sasukebo/go-micro/v4/auth"
    11  	"gitee.com/sasukebo/go-micro/v4/broker"
    12  	"gitee.com/sasukebo/go-micro/v4/cache"
    13  	"gitee.com/sasukebo/go-micro/v4/client"
    14  	"gitee.com/sasukebo/go-micro/v4/config"
    15  	"gitee.com/sasukebo/go-micro/v4/debug/profile"
    16  	"gitee.com/sasukebo/go-micro/v4/debug/profile/http"
    17  	"gitee.com/sasukebo/go-micro/v4/debug/profile/pprof"
    18  	"gitee.com/sasukebo/go-micro/v4/debug/trace"
    19  	"gitee.com/sasukebo/go-micro/v4/logger"
    20  	"gitee.com/sasukebo/go-micro/v4/registry"
    21  	"gitee.com/sasukebo/go-micro/v4/runtime"
    22  	"gitee.com/sasukebo/go-micro/v4/selector"
    23  	"gitee.com/sasukebo/go-micro/v4/server"
    24  	"gitee.com/sasukebo/go-micro/v4/store"
    25  	"gitee.com/sasukebo/go-micro/v4/transport"
    26  	"github.com/urfave/cli/v2"
    27  )
    28  
    29  type Cmd interface {
    30  	// The cli app within this cmd
    31  	App() *cli.App
    32  	// Adds options, parses flags and initialise
    33  	// exits on error
    34  	Init(opts ...Option) error
    35  	// Options set within this command
    36  	Options() Options
    37  }
    38  
    39  type cmd struct {
    40  	opts Options
    41  	app  *cli.App
    42  }
    43  
    44  type Option func(o *Options)
    45  
    46  var (
    47  	DefaultCmd = newCmd()
    48  
    49  	DefaultFlags = []cli.Flag{
    50  		&cli.StringFlag{
    51  			Name:    "client",
    52  			EnvVars: []string{"MICRO_CLIENT"},
    53  			Usage:   "Client for go-micro; rpc",
    54  		},
    55  		&cli.StringFlag{
    56  			Name:    "client_request_timeout",
    57  			EnvVars: []string{"MICRO_CLIENT_REQUEST_TIMEOUT"},
    58  			Usage:   "Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s",
    59  		},
    60  		&cli.IntFlag{
    61  			Name:    "client_retries",
    62  			EnvVars: []string{"MICRO_CLIENT_RETRIES"},
    63  			Value:   client.DefaultRetries,
    64  			Usage:   "Sets the client retries. Default: 1",
    65  		},
    66  		&cli.IntFlag{
    67  			Name:    "client_pool_size",
    68  			EnvVars: []string{"MICRO_CLIENT_POOL_SIZE"},
    69  			Usage:   "Sets the client connection pool size. Default: 1",
    70  		},
    71  		&cli.StringFlag{
    72  			Name:    "client_pool_ttl",
    73  			EnvVars: []string{"MICRO_CLIENT_POOL_TTL"},
    74  			Usage:   "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m",
    75  		},
    76  		&cli.IntFlag{
    77  			Name:    "register_ttl",
    78  			EnvVars: []string{"MICRO_REGISTER_TTL"},
    79  			Value:   60,
    80  			Usage:   "Register TTL in seconds",
    81  		},
    82  		&cli.IntFlag{
    83  			Name:    "register_interval",
    84  			EnvVars: []string{"MICRO_REGISTER_INTERVAL"},
    85  			Value:   30,
    86  			Usage:   "Register interval in seconds",
    87  		},
    88  		&cli.StringFlag{
    89  			Name:    "server",
    90  			EnvVars: []string{"MICRO_SERVER"},
    91  			Usage:   "Server for go-micro; rpc",
    92  		},
    93  		&cli.StringFlag{
    94  			Name:    "server_name",
    95  			EnvVars: []string{"MICRO_SERVER_NAME"},
    96  			Usage:   "Name of the server. go.micro.srv.example",
    97  		},
    98  		&cli.StringFlag{
    99  			Name:    "server_version",
   100  			EnvVars: []string{"MICRO_SERVER_VERSION"},
   101  			Usage:   "Version of the server. 1.1.0",
   102  		},
   103  		&cli.StringFlag{
   104  			Name:    "server_id",
   105  			EnvVars: []string{"MICRO_SERVER_ID"},
   106  			Usage:   "Id of the server. Auto-generated if not specified",
   107  		},
   108  		&cli.StringFlag{
   109  			Name:    "server_address",
   110  			EnvVars: []string{"MICRO_SERVER_ADDRESS"},
   111  			Usage:   "Bind address for the server. 127.0.0.1:8080",
   112  		},
   113  		&cli.StringFlag{
   114  			Name:    "server_advertise",
   115  			EnvVars: []string{"MICRO_SERVER_ADVERTISE"},
   116  			Usage:   "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
   117  		},
   118  		&cli.StringSliceFlag{
   119  			Name:    "server_metadata",
   120  			EnvVars: []string{"MICRO_SERVER_METADATA"},
   121  			Value:   &cli.StringSlice{},
   122  			Usage:   "A list of key-value pairs defining metadata. version=1.0.0",
   123  		},
   124  		&cli.StringFlag{
   125  			Name:    "broker",
   126  			EnvVars: []string{"MICRO_BROKER"},
   127  			Usage:   "Broker for pub/sub. http, nats, rabbitmq",
   128  		},
   129  		&cli.StringFlag{
   130  			Name:    "broker_address",
   131  			EnvVars: []string{"MICRO_BROKER_ADDRESS"},
   132  			Usage:   "Comma-separated list of broker addresses",
   133  		},
   134  		&cli.StringFlag{
   135  			Name:    "profile",
   136  			Usage:   "Debug profiler for cpu and memory stats",
   137  			EnvVars: []string{"MICRO_DEBUG_PROFILE"},
   138  		},
   139  		&cli.StringFlag{
   140  			Name:    "registry",
   141  			EnvVars: []string{"MICRO_REGISTRY"},
   142  			Usage:   "Registry for discovery. etcd, mdns",
   143  		},
   144  		&cli.StringFlag{
   145  			Name:    "registry_address",
   146  			EnvVars: []string{"MICRO_REGISTRY_ADDRESS"},
   147  			Usage:   "Comma-separated list of registry addresses",
   148  		},
   149  		&cli.StringFlag{
   150  			Name:    "runtime",
   151  			Usage:   "Runtime for building and running services e.g local, kubernetes",
   152  			EnvVars: []string{"MICRO_RUNTIME"},
   153  		},
   154  		&cli.StringFlag{
   155  			Name:    "runtime_source",
   156  			Usage:   "Runtime source for building and running services e.g github.com/micro/service",
   157  			EnvVars: []string{"MICRO_RUNTIME_SOURCE"},
   158  			Value:   "github.com/micro/services",
   159  		},
   160  		&cli.StringFlag{
   161  			Name:    "selector",
   162  			EnvVars: []string{"MICRO_SELECTOR"},
   163  			Usage:   "Selector used to pick nodes for querying",
   164  		},
   165  		&cli.StringFlag{
   166  			Name:    "store",
   167  			EnvVars: []string{"MICRO_STORE"},
   168  			Usage:   "Store used for key-value storage",
   169  		},
   170  		&cli.StringFlag{
   171  			Name:    "store_address",
   172  			EnvVars: []string{"MICRO_STORE_ADDRESS"},
   173  			Usage:   "Comma-separated list of store addresses",
   174  		},
   175  		&cli.StringFlag{
   176  			Name:    "store_database",
   177  			EnvVars: []string{"MICRO_STORE_DATABASE"},
   178  			Usage:   "Database option for the underlying store",
   179  		},
   180  		&cli.StringFlag{
   181  			Name:    "store_table",
   182  			EnvVars: []string{"MICRO_STORE_TABLE"},
   183  			Usage:   "Table option for the underlying store",
   184  		},
   185  		&cli.StringFlag{
   186  			Name:    "transport",
   187  			EnvVars: []string{"MICRO_TRANSPORT"},
   188  			Usage:   "Transport mechanism used; http",
   189  		},
   190  		&cli.StringFlag{
   191  			Name:    "transport_address",
   192  			EnvVars: []string{"MICRO_TRANSPORT_ADDRESS"},
   193  			Usage:   "Comma-separated list of transport addresses",
   194  		},
   195  		&cli.StringFlag{
   196  			Name:    "tracer",
   197  			EnvVars: []string{"MICRO_TRACER"},
   198  			Usage:   "Tracer for distributed tracing, e.g. memory, jaeger",
   199  		},
   200  		&cli.StringFlag{
   201  			Name:    "tracer_address",
   202  			EnvVars: []string{"MICRO_TRACER_ADDRESS"},
   203  			Usage:   "Comma-separated list of tracer addresses",
   204  		},
   205  		&cli.StringFlag{
   206  			Name:    "auth",
   207  			EnvVars: []string{"MICRO_AUTH"},
   208  			Usage:   "Auth for role based access control, e.g. service",
   209  		},
   210  		&cli.StringFlag{
   211  			Name:    "auth_id",
   212  			EnvVars: []string{"MICRO_AUTH_ID"},
   213  			Usage:   "Account ID used for client authentication",
   214  		},
   215  		&cli.StringFlag{
   216  			Name:    "auth_secret",
   217  			EnvVars: []string{"MICRO_AUTH_SECRET"},
   218  			Usage:   "Account secret used for client authentication",
   219  		},
   220  		&cli.StringFlag{
   221  			Name:    "auth_namespace",
   222  			EnvVars: []string{"MICRO_AUTH_NAMESPACE"},
   223  			Usage:   "Namespace for the services auth account",
   224  			Value:   "go.micro",
   225  		},
   226  		&cli.StringFlag{
   227  			Name:    "auth_public_key",
   228  			EnvVars: []string{"MICRO_AUTH_PUBLIC_KEY"},
   229  			Usage:   "Public key for JWT auth (base64 encoded PEM)",
   230  		},
   231  		&cli.StringFlag{
   232  			Name:    "auth_private_key",
   233  			EnvVars: []string{"MICRO_AUTH_PRIVATE_KEY"},
   234  			Usage:   "Private key for JWT auth (base64 encoded PEM)",
   235  		},
   236  		&cli.StringFlag{
   237  			Name:    "config",
   238  			EnvVars: []string{"MICRO_CONFIG"},
   239  			Usage:   "The source of the config to be used to get configuration",
   240  		},
   241  	}
   242  
   243  	DefaultBrokers = map[string]func(...broker.Option) broker.Broker{}
   244  
   245  	DefaultClients = map[string]func(...client.Option) client.Client{}
   246  
   247  	DefaultRegistries = map[string]func(...registry.Option) registry.Registry{}
   248  
   249  	DefaultSelectors = map[string]func(...selector.Option) selector.Selector{}
   250  
   251  	DefaultServers = map[string]func(...server.Option) server.Server{}
   252  
   253  	DefaultTransports = map[string]func(...transport.Option) transport.Transport{}
   254  
   255  	DefaultRuntimes = map[string]func(...runtime.Option) runtime.Runtime{}
   256  
   257  	DefaultStores = map[string]func(...store.Option) store.Store{}
   258  
   259  	DefaultTracers = map[string]func(...trace.Option) trace.Tracer{}
   260  
   261  	DefaultAuths = map[string]func(...auth.Option) auth.Auth{}
   262  
   263  	DefaultProfiles = map[string]func(...profile.Option) profile.Profile{
   264  		"http":  http.NewProfile,
   265  		"pprof": pprof.NewProfile,
   266  	}
   267  
   268  	DefaultConfigs = map[string]func(...config.Option) (config.Config, error){}
   269  
   270  	DefaultCaches = map[string]func(...cache.Option) cache.Cache{}
   271  )
   272  
   273  func init() {
   274  	rand.Seed(time.Now().Unix())
   275  }
   276  
   277  func newCmd(opts ...Option) Cmd {
   278  	options := Options{
   279  		Auth:      &auth.DefaultAuth,
   280  		Broker:    &broker.DefaultBroker,
   281  		Client:    &client.DefaultClient,
   282  		Registry:  &registry.DefaultRegistry,
   283  		Server:    &server.DefaultServer,
   284  		Selector:  &selector.DefaultSelector,
   285  		Transport: &transport.DefaultTransport,
   286  		Runtime:   &runtime.DefaultRuntime,
   287  		Store:     &store.DefaultStore,
   288  		Tracer:    &trace.DefaultTracer,
   289  		Profile:   &profile.DefaultProfile,
   290  		Config:    &config.DefaultConfig,
   291  		Cache:     &cache.DefaultCache,
   292  
   293  		Brokers:    DefaultBrokers,
   294  		Clients:    DefaultClients,
   295  		Registries: DefaultRegistries,
   296  		Selectors:  DefaultSelectors,
   297  		Servers:    DefaultServers,
   298  		Transports: DefaultTransports,
   299  		Runtimes:   DefaultRuntimes,
   300  		Stores:     DefaultStores,
   301  		Tracers:    DefaultTracers,
   302  		Auths:      DefaultAuths,
   303  		Profiles:   DefaultProfiles,
   304  		Configs:    DefaultConfigs,
   305  		Caches:     DefaultCaches,
   306  	}
   307  
   308  	for _, o := range opts {
   309  		o(&options)
   310  	}
   311  
   312  	if len(options.Description) == 0 {
   313  		options.Description = "a go-micro service"
   314  	}
   315  
   316  	cmd := new(cmd)
   317  	cmd.opts = options
   318  	cmd.app = cli.NewApp()
   319  	cmd.app.Name = cmd.opts.Name
   320  	cmd.app.Version = cmd.opts.Version
   321  	cmd.app.Usage = cmd.opts.Description
   322  	cmd.app.Before = cmd.Before
   323  	cmd.app.Flags = DefaultFlags
   324  	cmd.app.Action = func(c *cli.Context) error {
   325  		return nil
   326  	}
   327  
   328  	if len(options.Version) == 0 {
   329  		cmd.app.HideVersion = true
   330  	}
   331  
   332  	return cmd
   333  }
   334  
   335  func (c *cmd) App() *cli.App {
   336  	return c.app
   337  }
   338  
   339  func (c *cmd) Options() Options {
   340  	return c.opts
   341  }
   342  
   343  func (c *cmd) Before(ctx *cli.Context) error {
   344  	// If flags are set then use them otherwise do nothing
   345  	var serverOpts []server.Option
   346  	var clientOpts []client.Option
   347  
   348  	// Set the client
   349  	if name := ctx.String("client"); len(name) > 0 {
   350  		// only change if we have the client and type differs
   351  		if cl, ok := c.opts.Clients[name]; ok && (*c.opts.Client).String() != name {
   352  			*c.opts.Client = cl()
   353  		}
   354  	}
   355  
   356  	// Set the server
   357  	if name := ctx.String("server"); len(name) > 0 {
   358  		// only change if we have the server and type differs
   359  		if s, ok := c.opts.Servers[name]; ok && (*c.opts.Server).String() != name {
   360  			*c.opts.Server = s()
   361  		}
   362  	}
   363  
   364  	// Set the store
   365  	if name := ctx.String("store"); len(name) > 0 {
   366  		s, ok := c.opts.Stores[name]
   367  		if !ok {
   368  			return fmt.Errorf("Unsupported store: %s", name)
   369  		}
   370  
   371  		*c.opts.Store = s(store.WithClient(*c.opts.Client))
   372  	}
   373  
   374  	// Set the runtime
   375  	if name := ctx.String("runtime"); len(name) > 0 {
   376  		r, ok := c.opts.Runtimes[name]
   377  		if !ok {
   378  			return fmt.Errorf("Unsupported runtime: %s", name)
   379  		}
   380  
   381  		*c.opts.Runtime = r(runtime.WithClient(*c.opts.Client))
   382  	}
   383  
   384  	// Set the tracer
   385  	if name := ctx.String("tracer"); len(name) > 0 {
   386  		r, ok := c.opts.Tracers[name]
   387  		if !ok {
   388  			return fmt.Errorf("Unsupported tracer: %s", name)
   389  		}
   390  
   391  		*c.opts.Tracer = r()
   392  	}
   393  
   394  	// Setup auth
   395  	authOpts := []auth.Option{}
   396  
   397  	if len(ctx.String("auth_id")) > 0 || len(ctx.String("auth_secret")) > 0 {
   398  		authOpts = append(authOpts, auth.Credentials(
   399  			ctx.String("auth_id"), ctx.String("auth_secret"),
   400  		))
   401  	}
   402  	if len(ctx.String("auth_public_key")) > 0 {
   403  		authOpts = append(authOpts, auth.PublicKey(ctx.String("auth_public_key")))
   404  	}
   405  	if len(ctx.String("auth_private_key")) > 0 {
   406  		authOpts = append(authOpts, auth.PrivateKey(ctx.String("auth_private_key")))
   407  	}
   408  	if len(ctx.String("auth_namespace")) > 0 {
   409  		authOpts = append(authOpts, auth.Namespace(ctx.String("auth_namespace")))
   410  	}
   411  	if name := ctx.String("auth"); len(name) > 0 {
   412  		r, ok := c.opts.Auths[name]
   413  		if !ok {
   414  			return fmt.Errorf("Unsupported auth: %s", name)
   415  		}
   416  
   417  		*c.opts.Auth = r(authOpts...)
   418  	}
   419  
   420  	// Set the registry
   421  	if name := ctx.String("registry"); len(name) > 0 && (*c.opts.Registry).String() != name {
   422  		r, ok := c.opts.Registries[name]
   423  		if !ok {
   424  			return fmt.Errorf("Registry %s not found", name)
   425  		}
   426  
   427  		*c.opts.Registry = r()
   428  		serverOpts = append(serverOpts, server.Registry(*c.opts.Registry))
   429  		clientOpts = append(clientOpts, client.Registry(*c.opts.Registry))
   430  
   431  		if err := (*c.opts.Selector).Init(selector.Registry(*c.opts.Registry)); err != nil {
   432  			logger.Fatalf("Error configuring registry: %v", err)
   433  		}
   434  
   435  		clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
   436  
   437  		if err := (*c.opts.Broker).Init(broker.Registry(*c.opts.Registry)); err != nil {
   438  			logger.Fatalf("Error configuring broker: %v", err)
   439  		}
   440  	}
   441  
   442  	// Set the profile
   443  	if name := ctx.String("profile"); len(name) > 0 {
   444  		p, ok := c.opts.Profiles[name]
   445  		if !ok {
   446  			return fmt.Errorf("Unsupported profile: %s", name)
   447  		}
   448  
   449  		*c.opts.Profile = p()
   450  	}
   451  
   452  	// Set the broker
   453  	if name := ctx.String("broker"); len(name) > 0 && (*c.opts.Broker).String() != name {
   454  		b, ok := c.opts.Brokers[name]
   455  		if !ok {
   456  			return fmt.Errorf("Broker %s not found", name)
   457  		}
   458  
   459  		*c.opts.Broker = b()
   460  		serverOpts = append(serverOpts, server.Broker(*c.opts.Broker))
   461  		clientOpts = append(clientOpts, client.Broker(*c.opts.Broker))
   462  	}
   463  
   464  	// Set the selector
   465  	if name := ctx.String("selector"); len(name) > 0 && (*c.opts.Selector).String() != name {
   466  		s, ok := c.opts.Selectors[name]
   467  		if !ok {
   468  			return fmt.Errorf("Selector %s not found", name)
   469  		}
   470  
   471  		*c.opts.Selector = s(selector.Registry(*c.opts.Registry))
   472  
   473  		// No server option here. Should there be?
   474  		clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
   475  	}
   476  
   477  	// Set the transport
   478  	if name := ctx.String("transport"); len(name) > 0 && (*c.opts.Transport).String() != name {
   479  		t, ok := c.opts.Transports[name]
   480  		if !ok {
   481  			return fmt.Errorf("Transport %s not found", name)
   482  		}
   483  
   484  		*c.opts.Transport = t()
   485  		serverOpts = append(serverOpts, server.Transport(*c.opts.Transport))
   486  		clientOpts = append(clientOpts, client.Transport(*c.opts.Transport))
   487  	}
   488  
   489  	// Parse the server options
   490  	metadata := make(map[string]string)
   491  	for _, d := range ctx.StringSlice("server_metadata") {
   492  		var key, val string
   493  		parts := strings.Split(d, "=")
   494  		key = parts[0]
   495  		if len(parts) > 1 {
   496  			val = strings.Join(parts[1:], "=")
   497  		}
   498  		metadata[key] = val
   499  	}
   500  
   501  	if len(metadata) > 0 {
   502  		serverOpts = append(serverOpts, server.Metadata(metadata))
   503  	}
   504  
   505  	if len(ctx.String("broker_address")) > 0 {
   506  		if err := (*c.opts.Broker).Init(broker.Addrs(strings.Split(ctx.String("broker_address"), ",")...)); err != nil {
   507  			logger.Fatalf("Error configuring broker: %v", err)
   508  		}
   509  	}
   510  
   511  	if len(ctx.String("registry_address")) > 0 {
   512  		if err := (*c.opts.Registry).Init(registry.Addrs(strings.Split(ctx.String("registry_address"), ",")...)); err != nil {
   513  			logger.Fatalf("Error configuring registry: %v", err)
   514  		}
   515  	}
   516  
   517  	if len(ctx.String("transport_address")) > 0 {
   518  		if err := (*c.opts.Transport).Init(transport.Addrs(strings.Split(ctx.String("transport_address"), ",")...)); err != nil {
   519  			logger.Fatalf("Error configuring transport: %v", err)
   520  		}
   521  	}
   522  
   523  	if len(ctx.String("store_address")) > 0 {
   524  		if err := (*c.opts.Store).Init(store.Nodes(strings.Split(ctx.String("store_address"), ",")...)); err != nil {
   525  			logger.Fatalf("Error configuring store: %v", err)
   526  		}
   527  	}
   528  
   529  	if len(ctx.String("store_database")) > 0 {
   530  		if err := (*c.opts.Store).Init(store.Database(ctx.String("store_database"))); err != nil {
   531  			logger.Fatalf("Error configuring store database option: %v", err)
   532  		}
   533  	}
   534  
   535  	if len(ctx.String("store_table")) > 0 {
   536  		if err := (*c.opts.Store).Init(store.Table(ctx.String("store_table"))); err != nil {
   537  			logger.Fatalf("Error configuring store table option: %v", err)
   538  		}
   539  	}
   540  
   541  	if len(ctx.String("server_name")) > 0 {
   542  		serverOpts = append(serverOpts, server.Name(ctx.String("server_name")))
   543  	}
   544  
   545  	if len(ctx.String("server_version")) > 0 {
   546  		serverOpts = append(serverOpts, server.Version(ctx.String("server_version")))
   547  	}
   548  
   549  	if len(ctx.String("server_id")) > 0 {
   550  		serverOpts = append(serverOpts, server.Id(ctx.String("server_id")))
   551  	}
   552  
   553  	if len(ctx.String("server_address")) > 0 {
   554  		serverOpts = append(serverOpts, server.Address(ctx.String("server_address")))
   555  	}
   556  
   557  	if len(ctx.String("server_advertise")) > 0 {
   558  		serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
   559  	}
   560  
   561  	if ttl := time.Duration(ctx.Int("register_ttl")); ttl >= 0 {
   562  		serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second))
   563  	}
   564  
   565  	if val := time.Duration(ctx.Int("register_interval")); val >= 0 {
   566  		serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
   567  	}
   568  
   569  	if len(ctx.String("runtime_source")) > 0 {
   570  		if err := (*c.opts.Runtime).Init(runtime.WithSource(ctx.String("runtime_source"))); err != nil {
   571  			logger.Fatalf("Error configuring runtime: %v", err)
   572  		}
   573  	}
   574  
   575  	// client opts
   576  	if r := ctx.Int("client_retries"); r >= 0 {
   577  		clientOpts = append(clientOpts, client.Retries(r))
   578  	}
   579  
   580  	if t := ctx.String("client_request_timeout"); len(t) > 0 {
   581  		d, err := time.ParseDuration(t)
   582  		if err != nil {
   583  			return fmt.Errorf("failed to parse client_request_timeout: %v", t)
   584  		}
   585  		clientOpts = append(clientOpts, client.RequestTimeout(d))
   586  	}
   587  
   588  	if r := ctx.Int("client_pool_size"); r > 0 {
   589  		clientOpts = append(clientOpts, client.PoolSize(r))
   590  	}
   591  
   592  	if t := ctx.String("client_pool_ttl"); len(t) > 0 {
   593  		d, err := time.ParseDuration(t)
   594  		if err != nil {
   595  			return fmt.Errorf("failed to parse client_pool_ttl: %v", t)
   596  		}
   597  		clientOpts = append(clientOpts, client.PoolTTL(d))
   598  	}
   599  
   600  	// We have some command line opts for the server.
   601  	// Lets set it up
   602  	if len(serverOpts) > 0 {
   603  		if err := (*c.opts.Server).Init(serverOpts...); err != nil {
   604  			logger.Fatalf("Error configuring server: %v", err)
   605  		}
   606  	}
   607  
   608  	// Use an init option?
   609  	if len(clientOpts) > 0 {
   610  		if err := (*c.opts.Client).Init(clientOpts...); err != nil {
   611  			logger.Fatalf("Error configuring client: %v", err)
   612  		}
   613  	}
   614  
   615  	// config
   616  	if name := ctx.String("config"); len(name) > 0 {
   617  		// only change if we have the server and type differs
   618  		if r, ok := c.opts.Configs[name]; ok {
   619  			rc, err := r()
   620  			if err != nil {
   621  				logger.Fatalf("Error configuring config: %v", err)
   622  			}
   623  			*c.opts.Config = rc
   624  		}
   625  	}
   626  
   627  	return nil
   628  }
   629  
   630  func (c *cmd) Init(opts ...Option) error {
   631  	for _, o := range opts {
   632  		o(&c.opts)
   633  	}
   634  	if len(c.opts.Name) > 0 {
   635  		c.app.Name = c.opts.Name
   636  	}
   637  	if len(c.opts.Version) > 0 {
   638  		c.app.Version = c.opts.Version
   639  	}
   640  	c.app.HideVersion = len(c.opts.Version) == 0
   641  	c.app.Usage = c.opts.Description
   642  	c.app.RunAndExitOnError()
   643  	return nil
   644  }
   645  
   646  func DefaultOptions() Options {
   647  	return DefaultCmd.Options()
   648  }
   649  
   650  func App() *cli.App {
   651  	return DefaultCmd.App()
   652  }
   653  
   654  func Init(opts ...Option) error {
   655  	return DefaultCmd.Init(opts...)
   656  }
   657  
   658  func NewCmd(opts ...Option) Cmd {
   659  	return newCmd(opts...)
   660  }