github.com/annwntech/go-micro/v2@v2.9.5/config/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  	"github.com/annwntech/go-micro/v2/auth"
    11  	"github.com/annwntech/go-micro/v2/auth/provider"
    12  	"github.com/annwntech/go-micro/v2/broker"
    13  	"github.com/annwntech/go-micro/v2/client"
    14  	"github.com/annwntech/go-micro/v2/client/grpc"
    15  	"github.com/annwntech/go-micro/v2/client/selector"
    16  	"github.com/annwntech/go-micro/v2/config"
    17  	configSrc "github.com/annwntech/go-micro/v2/config/source"
    18  	configSrv "github.com/annwntech/go-micro/v2/config/source/service"
    19  	"github.com/annwntech/go-micro/v2/debug/profile"
    20  	"github.com/annwntech/go-micro/v2/debug/profile/http"
    21  	"github.com/annwntech/go-micro/v2/debug/profile/pprof"
    22  	"github.com/annwntech/go-micro/v2/debug/trace"
    23  	"github.com/annwntech/go-micro/v2/logger"
    24  	"github.com/annwntech/go-micro/v2/registry"
    25  	registrySrv "github.com/annwntech/go-micro/v2/registry/service"
    26  	"github.com/annwntech/go-micro/v2/runtime"
    27  	"github.com/annwntech/go-micro/v2/server"
    28  	"github.com/annwntech/go-micro/v2/store"
    29  	"github.com/annwntech/go-micro/v2/transport"
    30  	authutil "github.com/annwntech/go-micro/v2/util/auth"
    31  	"github.com/annwntech/go-micro/v2/util/wrapper"
    32  
    33  	// clients
    34  	cgrpc "github.com/annwntech/go-micro/v2/client/grpc"
    35  	cmucp "github.com/annwntech/go-micro/v2/client/mucp"
    36  
    37  	// servers
    38  	"github.com/micro/cli/v2"
    39  
    40  	sgrpc "github.com/annwntech/go-micro/v2/server/grpc"
    41  	smucp "github.com/annwntech/go-micro/v2/server/mucp"
    42  
    43  	// brokers
    44  	brokerHttp "github.com/annwntech/go-micro/v2/broker/http"
    45  	"github.com/annwntech/go-micro/v2/broker/memory"
    46  	"github.com/annwntech/go-micro/v2/broker/nats"
    47  	brokerSrv "github.com/annwntech/go-micro/v2/broker/service"
    48  
    49  	// registries
    50  	"github.com/annwntech/go-micro/v2/registry/etcd"
    51  	"github.com/annwntech/go-micro/v2/registry/mdns"
    52  	rmem "github.com/annwntech/go-micro/v2/registry/memory"
    53  	regSrv "github.com/annwntech/go-micro/v2/registry/service"
    54  
    55  	// runtimes
    56  	kRuntime "github.com/annwntech/go-micro/v2/runtime/kubernetes"
    57  	lRuntime "github.com/annwntech/go-micro/v2/runtime/local"
    58  	srvRuntime "github.com/annwntech/go-micro/v2/runtime/service"
    59  
    60  	// selectors
    61  	"github.com/annwntech/go-micro/v2/client/selector/dns"
    62  	"github.com/annwntech/go-micro/v2/client/selector/router"
    63  	"github.com/annwntech/go-micro/v2/client/selector/static"
    64  
    65  	// transports
    66  	thttp "github.com/annwntech/go-micro/v2/transport/http"
    67  	tmem "github.com/annwntech/go-micro/v2/transport/memory"
    68  
    69  	// stores
    70  	memStore "github.com/annwntech/go-micro/v2/store/memory"
    71  	svcStore "github.com/annwntech/go-micro/v2/store/service"
    72  
    73  	// tracers
    74  	// jTracer "github.com/annwntech/go-micro/v2/debug/trace/jaeger"
    75  	memTracer "github.com/annwntech/go-micro/v2/debug/trace/memory"
    76  
    77  	// auth
    78  	jwtAuth "github.com/annwntech/go-micro/v2/auth/jwt"
    79  	svcAuth "github.com/annwntech/go-micro/v2/auth/service"
    80  
    81  	// auth providers
    82  	"github.com/annwntech/go-micro/v2/auth/provider/basic"
    83  	"github.com/annwntech/go-micro/v2/auth/provider/oauth"
    84  )
    85  
    86  type Cmd interface {
    87  	// The cli app within this cmd
    88  	App() *cli.App
    89  	// Adds options, parses flags and initialise
    90  	// exits on error
    91  	Init(opts ...Option) error
    92  	// Options set within this command
    93  	Options() Options
    94  }
    95  
    96  type cmd struct {
    97  	opts Options
    98  	app  *cli.App
    99  }
   100  
   101  type Option func(o *Options)
   102  
   103  var (
   104  	DefaultCmd = newCmd()
   105  
   106  	DefaultFlags = []cli.Flag{
   107  		&cli.StringFlag{
   108  			Name:    "client",
   109  			EnvVars: []string{"MICRO_CLIENT"},
   110  			Usage:   "Client for go-micro; rpc",
   111  		},
   112  		&cli.StringFlag{
   113  			Name:    "client_request_timeout",
   114  			EnvVars: []string{"MICRO_CLIENT_REQUEST_TIMEOUT"},
   115  			Usage:   "Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s",
   116  		},
   117  		&cli.IntFlag{
   118  			Name:    "client_retries",
   119  			EnvVars: []string{"MICRO_CLIENT_RETRIES"},
   120  			Value:   client.DefaultRetries,
   121  			Usage:   "Sets the client retries. Default: 1",
   122  		},
   123  		&cli.IntFlag{
   124  			Name:    "client_pool_size",
   125  			EnvVars: []string{"MICRO_CLIENT_POOL_SIZE"},
   126  			Usage:   "Sets the client connection pool size. Default: 1",
   127  		},
   128  		&cli.StringFlag{
   129  			Name:    "client_pool_ttl",
   130  			EnvVars: []string{"MICRO_CLIENT_POOL_TTL"},
   131  			Usage:   "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m",
   132  		},
   133  		&cli.IntFlag{
   134  			Name:    "register_ttl",
   135  			EnvVars: []string{"MICRO_REGISTER_TTL"},
   136  			Value:   60,
   137  			Usage:   "Register TTL in seconds",
   138  		},
   139  		&cli.IntFlag{
   140  			Name:    "register_interval",
   141  			EnvVars: []string{"MICRO_REGISTER_INTERVAL"},
   142  			Value:   30,
   143  			Usage:   "Register interval in seconds",
   144  		},
   145  		&cli.StringFlag{
   146  			Name:    "server",
   147  			EnvVars: []string{"MICRO_SERVER"},
   148  			Usage:   "Server for go-micro; rpc",
   149  		},
   150  		&cli.StringFlag{
   151  			Name:    "server_name",
   152  			EnvVars: []string{"MICRO_SERVER_NAME"},
   153  			Usage:   "Name of the server. go.micro.srv.example",
   154  		},
   155  		&cli.StringFlag{
   156  			Name:    "server_version",
   157  			EnvVars: []string{"MICRO_SERVER_VERSION"},
   158  			Usage:   "Version of the server. 1.1.0",
   159  		},
   160  		&cli.StringFlag{
   161  			Name:    "server_id",
   162  			EnvVars: []string{"MICRO_SERVER_ID"},
   163  			Usage:   "Id of the server. Auto-generated if not specified",
   164  		},
   165  		&cli.StringFlag{
   166  			Name:    "server_address",
   167  			EnvVars: []string{"MICRO_SERVER_ADDRESS"},
   168  			Usage:   "Bind address for the server. 127.0.0.1:8080",
   169  		},
   170  		&cli.StringFlag{
   171  			Name:    "server_advertise",
   172  			EnvVars: []string{"MICRO_SERVER_ADVERTISE"},
   173  			Usage:   "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
   174  		},
   175  		&cli.StringSliceFlag{
   176  			Name:    "server_metadata",
   177  			EnvVars: []string{"MICRO_SERVER_METADATA"},
   178  			Value:   &cli.StringSlice{},
   179  			Usage:   "A list of key-value pairs defining metadata. version=1.0.0",
   180  		},
   181  		&cli.StringFlag{
   182  			Name:    "broker",
   183  			EnvVars: []string{"MICRO_BROKER"},
   184  			Usage:   "Broker for pub/sub. http, nats, rabbitmq",
   185  		},
   186  		&cli.StringFlag{
   187  			Name:    "broker_address",
   188  			EnvVars: []string{"MICRO_BROKER_ADDRESS"},
   189  			Usage:   "Comma-separated list of broker addresses",
   190  		},
   191  		&cli.StringFlag{
   192  			Name:    "profile",
   193  			Usage:   "Debug profiler for cpu and memory stats",
   194  			EnvVars: []string{"MICRO_DEBUG_PROFILE"},
   195  		},
   196  		&cli.StringFlag{
   197  			Name:    "registry",
   198  			EnvVars: []string{"MICRO_REGISTRY"},
   199  			Usage:   "Registry for discovery. etcd, mdns",
   200  		},
   201  		&cli.StringFlag{
   202  			Name:    "registry_address",
   203  			EnvVars: []string{"MICRO_REGISTRY_ADDRESS"},
   204  			Usage:   "Comma-separated list of registry addresses",
   205  		},
   206  		&cli.StringFlag{
   207  			Name:    "runtime",
   208  			Usage:   "Runtime for building and running services e.g local, kubernetes",
   209  			EnvVars: []string{"MICRO_RUNTIME"},
   210  			Value:   "local",
   211  		},
   212  		&cli.StringFlag{
   213  			Name:    "runtime_source",
   214  			Usage:   "Runtime source for building and running services e.g github.com/micro/service",
   215  			EnvVars: []string{"MICRO_RUNTIME_SOURCE"},
   216  			Value:   "github.com/micro/services",
   217  		},
   218  		&cli.StringFlag{
   219  			Name:    "selector",
   220  			EnvVars: []string{"MICRO_SELECTOR"},
   221  			Usage:   "Selector used to pick nodes for querying",
   222  		},
   223  		&cli.StringFlag{
   224  			Name:    "store",
   225  			EnvVars: []string{"MICRO_STORE"},
   226  			Usage:   "Store used for key-value storage",
   227  		},
   228  		&cli.StringFlag{
   229  			Name:    "store_address",
   230  			EnvVars: []string{"MICRO_STORE_ADDRESS"},
   231  			Usage:   "Comma-separated list of store addresses",
   232  		},
   233  		&cli.StringFlag{
   234  			Name:    "store_database",
   235  			EnvVars: []string{"MICRO_STORE_DATABASE"},
   236  			Usage:   "Database option for the underlying store",
   237  		},
   238  		&cli.StringFlag{
   239  			Name:    "store_table",
   240  			EnvVars: []string{"MICRO_STORE_TABLE"},
   241  			Usage:   "Table option for the underlying store",
   242  		},
   243  		&cli.StringFlag{
   244  			Name:    "transport",
   245  			EnvVars: []string{"MICRO_TRANSPORT"},
   246  			Usage:   "Transport mechanism used; http",
   247  		},
   248  		&cli.StringFlag{
   249  			Name:    "transport_address",
   250  			EnvVars: []string{"MICRO_TRANSPORT_ADDRESS"},
   251  			Usage:   "Comma-separated list of transport addresses",
   252  		},
   253  		&cli.StringFlag{
   254  			Name:    "tracer",
   255  			EnvVars: []string{"MICRO_TRACER"},
   256  			Usage:   "Tracer for distributed tracing, e.g. memory, jaeger",
   257  		},
   258  		&cli.StringFlag{
   259  			Name:    "tracer_address",
   260  			EnvVars: []string{"MICRO_TRACER_ADDRESS"},
   261  			Usage:   "Comma-separated list of tracer addresses",
   262  		},
   263  		&cli.StringFlag{
   264  			Name:    "auth",
   265  			EnvVars: []string{"MICRO_AUTH"},
   266  			Usage:   "Auth for role based access control, e.g. service",
   267  		},
   268  		&cli.StringFlag{
   269  			Name:    "auth_id",
   270  			EnvVars: []string{"MICRO_AUTH_ID"},
   271  			Usage:   "Account ID used for client authentication",
   272  		},
   273  		&cli.StringFlag{
   274  			Name:    "auth_secret",
   275  			EnvVars: []string{"MICRO_AUTH_SECRET"},
   276  			Usage:   "Account secret used for client authentication",
   277  		},
   278  		&cli.StringFlag{
   279  			Name:    "auth_namespace",
   280  			EnvVars: []string{"MICRO_AUTH_NAMESPACE"},
   281  			Usage:   "Namespace for the services auth account",
   282  			Value:   "go.micro",
   283  		},
   284  		&cli.StringFlag{
   285  			Name:    "auth_public_key",
   286  			EnvVars: []string{"MICRO_AUTH_PUBLIC_KEY"},
   287  			Usage:   "Public key for JWT auth (base64 encoded PEM)",
   288  		},
   289  		&cli.StringFlag{
   290  			Name:    "auth_private_key",
   291  			EnvVars: []string{"MICRO_AUTH_PRIVATE_KEY"},
   292  			Usage:   "Private key for JWT auth (base64 encoded PEM)",
   293  		},
   294  		&cli.StringFlag{
   295  			Name:    "auth_provider",
   296  			EnvVars: []string{"MICRO_AUTH_PROVIDER"},
   297  			Usage:   "Auth provider used to login user",
   298  		},
   299  		&cli.StringFlag{
   300  			Name:    "auth_provider_client_id",
   301  			EnvVars: []string{"MICRO_AUTH_PROVIDER_CLIENT_ID"},
   302  			Usage:   "The client id to be used for oauth",
   303  		},
   304  		&cli.StringFlag{
   305  			Name:    "auth_provider_client_secret",
   306  			EnvVars: []string{"MICRO_AUTH_PROVIDER_CLIENT_SECRET"},
   307  			Usage:   "The client secret to be used for oauth",
   308  		},
   309  		&cli.StringFlag{
   310  			Name:    "auth_provider_endpoint",
   311  			EnvVars: []string{"MICRO_AUTH_PROVIDER_ENDPOINT"},
   312  			Usage:   "The enpoint to be used for oauth",
   313  		},
   314  		&cli.StringFlag{
   315  			Name:    "auth_provider_redirect",
   316  			EnvVars: []string{"MICRO_AUTH_PROVIDER_REDIRECT"},
   317  			Usage:   "The redirect to be used for oauth",
   318  		},
   319  		&cli.StringFlag{
   320  			Name:    "auth_provider_scope",
   321  			EnvVars: []string{"MICRO_AUTH_PROVIDER_SCOPE"},
   322  			Usage:   "The scope to be used for oauth",
   323  		},
   324  		&cli.StringFlag{
   325  			Name:    "config",
   326  			EnvVars: []string{"MICRO_CONFIG"},
   327  			Usage:   "The source of the config to be used to get configuration",
   328  		},
   329  	}
   330  
   331  	DefaultBrokers = map[string]func(...broker.Option) broker.Broker{
   332  		"service": brokerSrv.NewBroker,
   333  		"memory":  memory.NewBroker,
   334  		"nats":    nats.NewBroker,
   335  		"http":    brokerHttp.NewBroker,
   336  	}
   337  
   338  	DefaultClients = map[string]func(...client.Option) client.Client{
   339  		"mucp": cmucp.NewClient,
   340  		"grpc": cgrpc.NewClient,
   341  	}
   342  
   343  	DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
   344  		"service": regSrv.NewRegistry,
   345  		"etcd":    etcd.NewRegistry,
   346  		"mdns":    mdns.NewRegistry,
   347  		"memory":  rmem.NewRegistry,
   348  	}
   349  
   350  	DefaultSelectors = map[string]func(...selector.Option) selector.Selector{
   351  		"dns":    dns.NewSelector,
   352  		"router": router.NewSelector,
   353  		"static": static.NewSelector,
   354  	}
   355  
   356  	DefaultServers = map[string]func(...server.Option) server.Server{
   357  		"mucp": smucp.NewServer,
   358  		"grpc": sgrpc.NewServer,
   359  	}
   360  
   361  	DefaultTransports = map[string]func(...transport.Option) transport.Transport{
   362  		"memory": tmem.NewTransport,
   363  		"http":   thttp.NewTransport,
   364  	}
   365  
   366  	DefaultRuntimes = map[string]func(...runtime.Option) runtime.Runtime{
   367  		"local":      lRuntime.NewRuntime,
   368  		"service":    srvRuntime.NewRuntime,
   369  		"kubernetes": kRuntime.NewRuntime,
   370  	}
   371  
   372  	DefaultStores = map[string]func(...store.Option) store.Store{
   373  		"memory":  memStore.NewStore,
   374  		"service": svcStore.NewStore,
   375  	}
   376  
   377  	DefaultTracers = map[string]func(...trace.Option) trace.Tracer{
   378  		"memory": memTracer.NewTracer,
   379  		// "jaeger": jTracer.NewTracer,
   380  	}
   381  
   382  	DefaultAuths = map[string]func(...auth.Option) auth.Auth{
   383  		"service": svcAuth.NewAuth,
   384  		"jwt":     jwtAuth.NewAuth,
   385  	}
   386  
   387  	DefaultAuthProviders = map[string]func(...provider.Option) provider.Provider{
   388  		"oauth": oauth.NewProvider,
   389  		"basic": basic.NewProvider,
   390  	}
   391  
   392  	DefaultProfiles = map[string]func(...profile.Option) profile.Profile{
   393  		"http":  http.NewProfile,
   394  		"pprof": pprof.NewProfile,
   395  	}
   396  
   397  	DefaultConfigs = map[string]func(...config.Option) (config.Config, error){
   398  		"service": config.NewConfig,
   399  	}
   400  )
   401  
   402  func init() {
   403  	rand.Seed(time.Now().Unix())
   404  }
   405  
   406  func newCmd(opts ...Option) Cmd {
   407  	options := Options{
   408  		Auth:      &auth.DefaultAuth,
   409  		Broker:    &broker.DefaultBroker,
   410  		Client:    &client.DefaultClient,
   411  		Registry:  &registry.DefaultRegistry,
   412  		Server:    &server.DefaultServer,
   413  		Selector:  &selector.DefaultSelector,
   414  		Transport: &transport.DefaultTransport,
   415  		Runtime:   &runtime.DefaultRuntime,
   416  		Store:     &store.DefaultStore,
   417  		Tracer:    &trace.DefaultTracer,
   418  		Profile:   &profile.DefaultProfile,
   419  		Config:    &config.DefaultConfig,
   420  
   421  		Brokers:    DefaultBrokers,
   422  		Clients:    DefaultClients,
   423  		Registries: DefaultRegistries,
   424  		Selectors:  DefaultSelectors,
   425  		Servers:    DefaultServers,
   426  		Transports: DefaultTransports,
   427  		Runtimes:   DefaultRuntimes,
   428  		Stores:     DefaultStores,
   429  		Tracers:    DefaultTracers,
   430  		Auths:      DefaultAuths,
   431  		Profiles:   DefaultProfiles,
   432  		Configs:    DefaultConfigs,
   433  	}
   434  
   435  	for _, o := range opts {
   436  		o(&options)
   437  	}
   438  
   439  	if len(options.Description) == 0 {
   440  		options.Description = "a go-micro service"
   441  	}
   442  
   443  	cmd := new(cmd)
   444  	cmd.opts = options
   445  	cmd.app = cli.NewApp()
   446  	cmd.app.Name = cmd.opts.Name
   447  	cmd.app.Version = cmd.opts.Version
   448  	cmd.app.Usage = cmd.opts.Description
   449  	cmd.app.Before = cmd.Before
   450  	cmd.app.Flags = DefaultFlags
   451  	cmd.app.Action = func(c *cli.Context) error {
   452  		return nil
   453  	}
   454  
   455  	if len(options.Version) == 0 {
   456  		cmd.app.HideVersion = true
   457  	}
   458  
   459  	return cmd
   460  }
   461  
   462  func (c *cmd) App() *cli.App {
   463  	return c.app
   464  }
   465  
   466  func (c *cmd) Options() Options {
   467  	return c.opts
   468  }
   469  
   470  func (c *cmd) Before(ctx *cli.Context) error {
   471  	// If flags are set then use them otherwise do nothing
   472  	var serverOpts []server.Option
   473  	var clientOpts []client.Option
   474  
   475  	// setup a client to use when calling the runtime. It is important the auth client is wrapped
   476  	// after the cache client since the wrappers are applied in reverse order and the cache will use
   477  	// some of the headers set by the auth client.
   478  	authFn := func() auth.Auth { return *c.opts.Auth }
   479  	cacheFn := func() *client.Cache { return (*c.opts.Client).Options().Cache }
   480  	microClient := wrapper.CacheClient(cacheFn, grpc.NewClient())
   481  	microClient = wrapper.AuthClient(authFn, microClient)
   482  
   483  	// Set the store
   484  	if name := ctx.String("store"); len(name) > 0 {
   485  		s, ok := c.opts.Stores[name]
   486  		if !ok {
   487  			return fmt.Errorf("Unsupported store: %s", name)
   488  		}
   489  
   490  		*c.opts.Store = s(store.WithClient(microClient))
   491  	}
   492  
   493  	// Set the runtime
   494  	if name := ctx.String("runtime"); len(name) > 0 {
   495  		r, ok := c.opts.Runtimes[name]
   496  		if !ok {
   497  			return fmt.Errorf("Unsupported runtime: %s", name)
   498  		}
   499  
   500  		*c.opts.Runtime = r(runtime.WithClient(microClient))
   501  	}
   502  
   503  	// Set the tracer
   504  	if name := ctx.String("tracer"); len(name) > 0 {
   505  		r, ok := c.opts.Tracers[name]
   506  		if !ok {
   507  			return fmt.Errorf("Unsupported tracer: %s", name)
   508  		}
   509  
   510  		*c.opts.Tracer = r()
   511  	}
   512  
   513  	// Set the client
   514  	if name := ctx.String("client"); len(name) > 0 {
   515  		// only change if we have the client and type differs
   516  		if cl, ok := c.opts.Clients[name]; ok && (*c.opts.Client).String() != name {
   517  			*c.opts.Client = cl()
   518  		}
   519  	}
   520  
   521  	// Set the server
   522  	if name := ctx.String("server"); len(name) > 0 {
   523  		// only change if we have the server and type differs
   524  		if s, ok := c.opts.Servers[name]; ok && (*c.opts.Server).String() != name {
   525  			*c.opts.Server = s()
   526  		}
   527  	}
   528  
   529  	// Setup auth
   530  	authOpts := []auth.Option{}
   531  
   532  	if len(ctx.String("auth_id")) > 0 || len(ctx.String("auth_secret")) > 0 {
   533  		authOpts = append(authOpts, auth.Credentials(
   534  			ctx.String("auth_id"), ctx.String("auth_secret"),
   535  		))
   536  	}
   537  	if len(ctx.String("auth_public_key")) > 0 {
   538  		authOpts = append(authOpts, auth.PublicKey(ctx.String("auth_public_key")))
   539  	}
   540  	if len(ctx.String("auth_private_key")) > 0 {
   541  		authOpts = append(authOpts, auth.PrivateKey(ctx.String("auth_private_key")))
   542  	}
   543  	if len(ctx.String("auth_namespace")) > 0 {
   544  		authOpts = append(authOpts, auth.Issuer(ctx.String("auth_namespace")))
   545  	}
   546  	if name := ctx.String("auth_provider"); len(name) > 0 {
   547  		_, ok := DefaultAuthProviders[name]
   548  		if !ok {
   549  			return fmt.Errorf("AuthProvider %s not found", name)
   550  		}
   551  
   552  		var provOpts []provider.Option
   553  		clientID := ctx.String("auth_provider_client_id")
   554  		clientSecret := ctx.String("auth_provider_client_secret")
   555  		if len(clientID) > 0 || len(clientSecret) > 0 {
   556  			provOpts = append(provOpts, provider.Credentials(clientID, clientSecret))
   557  		}
   558  		if e := ctx.String("auth_provider_endpoint"); len(e) > 0 {
   559  			provOpts = append(provOpts, provider.Endpoint(e))
   560  		}
   561  		if r := ctx.String("auth_provider_redirect"); len(r) > 0 {
   562  			provOpts = append(provOpts, provider.Redirect(r))
   563  		}
   564  		if s := ctx.String("auth_provider_scope"); len(s) > 0 {
   565  			provOpts = append(provOpts, provider.Scope(s))
   566  		}
   567  
   568  		// authOpts = append(authOpts, auth.WithProvider(p(provOpts...)))
   569  	}
   570  
   571  	// Set the auth
   572  	if name := ctx.String("auth"); len(name) > 0 {
   573  		a, ok := c.opts.Auths[name]
   574  		if !ok {
   575  			return fmt.Errorf("Unsupported auth: %s", name)
   576  		}
   577  		*c.opts.Auth = a(authOpts...)
   578  		serverOpts = append(serverOpts, server.Auth(*c.opts.Auth))
   579  	} else {
   580  		(*c.opts.Auth).Init(authOpts...)
   581  	}
   582  
   583  	// Set the registry
   584  	if name := ctx.String("registry"); len(name) > 0 && (*c.opts.Registry).String() != name {
   585  		r, ok := c.opts.Registries[name]
   586  		if !ok {
   587  			return fmt.Errorf("Registry %s not found", name)
   588  		}
   589  
   590  		*c.opts.Registry = r(registrySrv.WithClient(microClient))
   591  		serverOpts = append(serverOpts, server.Registry(*c.opts.Registry))
   592  		clientOpts = append(clientOpts, client.Registry(*c.opts.Registry))
   593  
   594  		if err := (*c.opts.Selector).Init(selector.Registry(*c.opts.Registry)); err != nil {
   595  			logger.Fatalf("Error configuring registry: %v", err)
   596  		}
   597  
   598  		clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
   599  
   600  		if err := (*c.opts.Broker).Init(broker.Registry(*c.opts.Registry)); err != nil {
   601  			logger.Fatalf("Error configuring broker: %v", err)
   602  		}
   603  	}
   604  
   605  	// generate the services auth account
   606  	serverID := (*c.opts.Server).Options().Id
   607  	if err := authutil.Generate(serverID, c.App().Name, (*c.opts.Auth)); err != nil {
   608  		return err
   609  	}
   610  
   611  	// Set the profile
   612  	if name := ctx.String("profile"); len(name) > 0 {
   613  		p, ok := c.opts.Profiles[name]
   614  		if !ok {
   615  			return fmt.Errorf("Unsupported profile: %s", name)
   616  		}
   617  
   618  		*c.opts.Profile = p()
   619  	}
   620  
   621  	// Set the broker
   622  	if name := ctx.String("broker"); len(name) > 0 && (*c.opts.Broker).String() != name {
   623  		b, ok := c.opts.Brokers[name]
   624  		if !ok {
   625  			return fmt.Errorf("Broker %s not found", name)
   626  		}
   627  
   628  		*c.opts.Broker = b()
   629  		serverOpts = append(serverOpts, server.Broker(*c.opts.Broker))
   630  		clientOpts = append(clientOpts, client.Broker(*c.opts.Broker))
   631  	}
   632  
   633  	// Set the selector
   634  	if name := ctx.String("selector"); len(name) > 0 && (*c.opts.Selector).String() != name {
   635  		s, ok := c.opts.Selectors[name]
   636  		if !ok {
   637  			return fmt.Errorf("Selector %s not found", name)
   638  		}
   639  
   640  		*c.opts.Selector = s(selector.Registry(*c.opts.Registry))
   641  
   642  		// No server option here. Should there be?
   643  		clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
   644  	}
   645  
   646  	// Set the transport
   647  	if name := ctx.String("transport"); len(name) > 0 && (*c.opts.Transport).String() != name {
   648  		t, ok := c.opts.Transports[name]
   649  		if !ok {
   650  			return fmt.Errorf("Transport %s not found", name)
   651  		}
   652  
   653  		*c.opts.Transport = t()
   654  		serverOpts = append(serverOpts, server.Transport(*c.opts.Transport))
   655  		clientOpts = append(clientOpts, client.Transport(*c.opts.Transport))
   656  	}
   657  
   658  	// Parse the server options
   659  	metadata := make(map[string]string)
   660  	for _, d := range ctx.StringSlice("server_metadata") {
   661  		var key, val string
   662  		parts := strings.Split(d, "=")
   663  		key = parts[0]
   664  		if len(parts) > 1 {
   665  			val = strings.Join(parts[1:], "=")
   666  		}
   667  		metadata[key] = val
   668  	}
   669  
   670  	if len(metadata) > 0 {
   671  		serverOpts = append(serverOpts, server.Metadata(metadata))
   672  	}
   673  
   674  	if len(ctx.String("broker_address")) > 0 {
   675  		if err := (*c.opts.Broker).Init(broker.Addrs(strings.Split(ctx.String("broker_address"), ",")...)); err != nil {
   676  			logger.Fatalf("Error configuring broker: %v", err)
   677  		}
   678  	}
   679  
   680  	if len(ctx.String("registry_address")) > 0 {
   681  		if err := (*c.opts.Registry).Init(registry.Addrs(strings.Split(ctx.String("registry_address"), ",")...)); err != nil {
   682  			logger.Fatalf("Error configuring registry: %v", err)
   683  		}
   684  	}
   685  
   686  	if len(ctx.String("transport_address")) > 0 {
   687  		if err := (*c.opts.Transport).Init(transport.Addrs(strings.Split(ctx.String("transport_address"), ",")...)); err != nil {
   688  			logger.Fatalf("Error configuring transport: %v", err)
   689  		}
   690  	}
   691  
   692  	if len(ctx.String("store_address")) > 0 {
   693  		if err := (*c.opts.Store).Init(store.Nodes(strings.Split(ctx.String("store_address"), ",")...)); err != nil {
   694  			logger.Fatalf("Error configuring store: %v", err)
   695  		}
   696  	}
   697  
   698  	if len(ctx.String("store_database")) > 0 {
   699  		if err := (*c.opts.Store).Init(store.Database(ctx.String("store_database"))); err != nil {
   700  			logger.Fatalf("Error configuring store database option: %v", err)
   701  		}
   702  	}
   703  
   704  	if len(ctx.String("store_table")) > 0 {
   705  		if err := (*c.opts.Store).Init(store.Table(ctx.String("store_table"))); err != nil {
   706  			logger.Fatalf("Error configuring store table option: %v", err)
   707  		}
   708  	}
   709  
   710  	if len(ctx.String("server_name")) > 0 {
   711  		serverOpts = append(serverOpts, server.Name(ctx.String("server_name")))
   712  	}
   713  
   714  	if len(ctx.String("server_version")) > 0 {
   715  		serverOpts = append(serverOpts, server.Version(ctx.String("server_version")))
   716  	}
   717  
   718  	if len(ctx.String("server_id")) > 0 {
   719  		serverOpts = append(serverOpts, server.Id(ctx.String("server_id")))
   720  	}
   721  
   722  	if len(ctx.String("server_address")) > 0 {
   723  		serverOpts = append(serverOpts, server.Address(ctx.String("server_address")))
   724  	}
   725  
   726  	if len(ctx.String("server_advertise")) > 0 {
   727  		serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
   728  	}
   729  
   730  	if ttl := time.Duration(ctx.Int("register_ttl")); ttl >= 0 {
   731  		serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second))
   732  	}
   733  
   734  	if val := time.Duration(ctx.Int("register_interval")); val >= 0 {
   735  		serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
   736  	}
   737  
   738  	if len(ctx.String("runtime_source")) > 0 {
   739  		if err := (*c.opts.Runtime).Init(runtime.WithSource(ctx.String("runtime_source"))); err != nil {
   740  			logger.Fatalf("Error configuring runtime: %v", err)
   741  		}
   742  	}
   743  
   744  	if ctx.String("config") == "service" {
   745  		opt := config.WithSource(configSrv.NewSource(configSrc.WithClient(microClient)))
   746  		if err := (*c.opts.Config).Init(opt); err != nil {
   747  			logger.Fatalf("Error configuring config: %v", err)
   748  		}
   749  	}
   750  
   751  	// client opts
   752  	if r := ctx.Int("client_retries"); r >= 0 {
   753  		clientOpts = append(clientOpts, client.Retries(r))
   754  	}
   755  
   756  	if t := ctx.String("client_request_timeout"); len(t) > 0 {
   757  		d, err := time.ParseDuration(t)
   758  		if err != nil {
   759  			return fmt.Errorf("failed to parse client_request_timeout: %v", t)
   760  		}
   761  		clientOpts = append(clientOpts, client.RequestTimeout(d))
   762  	}
   763  
   764  	if r := ctx.Int("client_pool_size"); r > 0 {
   765  		clientOpts = append(clientOpts, client.PoolSize(r))
   766  	}
   767  
   768  	if t := ctx.String("client_pool_ttl"); len(t) > 0 {
   769  		d, err := time.ParseDuration(t)
   770  		if err != nil {
   771  			return fmt.Errorf("failed to parse client_pool_ttl: %v", t)
   772  		}
   773  		clientOpts = append(clientOpts, client.PoolTTL(d))
   774  	}
   775  
   776  	// We have some command line opts for the server.
   777  	// Lets set it up
   778  	if len(serverOpts) > 0 {
   779  		if err := (*c.opts.Server).Init(serverOpts...); err != nil {
   780  			logger.Fatalf("Error configuring server: %v", err)
   781  		}
   782  	}
   783  
   784  	// Use an init option?
   785  	if len(clientOpts) > 0 {
   786  		if err := (*c.opts.Client).Init(clientOpts...); err != nil {
   787  			logger.Fatalf("Error configuring client: %v", err)
   788  		}
   789  	}
   790  
   791  	return nil
   792  }
   793  
   794  func (c *cmd) Init(opts ...Option) error {
   795  	for _, o := range opts {
   796  		o(&c.opts)
   797  	}
   798  	if len(c.opts.Name) > 0 {
   799  		c.app.Name = c.opts.Name
   800  	}
   801  	if len(c.opts.Version) > 0 {
   802  		c.app.Version = c.opts.Version
   803  	}
   804  	c.app.HideVersion = len(c.opts.Version) == 0
   805  	c.app.Usage = c.opts.Description
   806  	c.app.RunAndExitOnError()
   807  	return nil
   808  }
   809  
   810  func DefaultOptions() Options {
   811  	return DefaultCmd.Options()
   812  }
   813  
   814  func App() *cli.App {
   815  	return DefaultCmd.App()
   816  }
   817  
   818  func Init(opts ...Option) error {
   819  	return DefaultCmd.Init(opts...)
   820  }
   821  
   822  func NewCmd(opts ...Option) Cmd {
   823  	return newCmd(opts...)
   824  }