gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/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  	"io"
     7  	"math/rand"
     8  	"os"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/micro/cli"
    13  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client"
    14  	cgrpc "gitee.com/liuxuezhan/go-micro-v1.18.0/client/grpc"
    15  	cmucp "gitee.com/liuxuezhan/go-micro-v1.18.0/client/mucp"
    16  	"gitee.com/liuxuezhan/go-micro-v1.18.0/server"
    17  	sgrpc "gitee.com/liuxuezhan/go-micro-v1.18.0/server/grpc"
    18  	smucp "gitee.com/liuxuezhan/go-micro-v1.18.0/server/mucp"
    19  	"gitee.com/liuxuezhan/go-micro-v1.18.0/util/log"
    20  
    21  	// brokers
    22  	"gitee.com/liuxuezhan/go-micro-v1.18.0/broker"
    23  	"gitee.com/liuxuezhan/go-micro-v1.18.0/broker/http"
    24  	"gitee.com/liuxuezhan/go-micro-v1.18.0/broker/memory"
    25  	"gitee.com/liuxuezhan/go-micro-v1.18.0/broker/nats"
    26  	brokerSrv "gitee.com/liuxuezhan/go-micro-v1.18.0/broker/service"
    27  
    28  	// registries
    29  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry"
    30  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry/etcd"
    31  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry/mdns"
    32  	rmem "gitee.com/liuxuezhan/go-micro-v1.18.0/registry/memory"
    33  	regSrv "gitee.com/liuxuezhan/go-micro-v1.18.0/registry/service"
    34  
    35  	// selectors
    36  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector"
    37  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector/dns"
    38  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector/router"
    39  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector/static"
    40  
    41  	// transports
    42  	"gitee.com/liuxuezhan/go-micro-v1.18.0/transport"
    43  	tgrpc "gitee.com/liuxuezhan/go-micro-v1.18.0/transport/grpc"
    44  	thttp "gitee.com/liuxuezhan/go-micro-v1.18.0/transport/http"
    45  	tmem "gitee.com/liuxuezhan/go-micro-v1.18.0/transport/memory"
    46  	"gitee.com/liuxuezhan/go-micro-v1.18.0/transport/quic"
    47  
    48  	// runtimes
    49  	"gitee.com/liuxuezhan/go-micro-v1.18.0/runtime"
    50  	"gitee.com/liuxuezhan/go-micro-v1.18.0/runtime/kubernetes"
    51  )
    52  
    53  type Cmd interface {
    54  	// The cli app within this cmd
    55  	App() *cli.App
    56  	// Adds options, parses flags and initialise
    57  	// exits on error
    58  	Init(opts ...Option) error
    59  	// Options set within this command
    60  	Options() Options
    61  }
    62  
    63  type cmd struct {
    64  	opts Options
    65  	app  *cli.App
    66  }
    67  
    68  type Option func(o *Options)
    69  
    70  var (
    71  	DefaultCmd = newCmd()
    72  
    73  	DefaultFlags = []cli.Flag{
    74  		cli.StringFlag{
    75  			Name:   "client",
    76  			EnvVar: "MICRO_CLIENT",
    77  			Usage:  "Client for go-micro; rpc",
    78  		},
    79  		cli.StringFlag{
    80  			Name:   "client_request_timeout",
    81  			EnvVar: "MICRO_CLIENT_REQUEST_TIMEOUT",
    82  			Usage:  "Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s",
    83  		},
    84  		cli.IntFlag{
    85  			Name:   "client_retries",
    86  			EnvVar: "MICRO_CLIENT_RETRIES",
    87  			Value:  client.DefaultRetries,
    88  			Usage:  "Sets the client retries. Default: 1",
    89  		},
    90  		cli.IntFlag{
    91  			Name:   "client_pool_size",
    92  			EnvVar: "MICRO_CLIENT_POOL_SIZE",
    93  			Usage:  "Sets the client connection pool size. Default: 1",
    94  		},
    95  		cli.StringFlag{
    96  			Name:   "client_pool_ttl",
    97  			EnvVar: "MICRO_CLIENT_POOL_TTL",
    98  			Usage:  "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m",
    99  		},
   100  		cli.IntFlag{
   101  			Name:   "register_ttl",
   102  			EnvVar: "MICRO_REGISTER_TTL",
   103  			Value:  60,
   104  			Usage:  "Register TTL in seconds",
   105  		},
   106  		cli.IntFlag{
   107  			Name:   "register_interval",
   108  			EnvVar: "MICRO_REGISTER_INTERVAL",
   109  			Value:  30,
   110  			Usage:  "Register interval in seconds",
   111  		},
   112  		cli.StringFlag{
   113  			Name:   "server",
   114  			EnvVar: "MICRO_SERVER",
   115  			Usage:  "Server for go-micro; rpc",
   116  		},
   117  		cli.StringFlag{
   118  			Name:   "server_name",
   119  			EnvVar: "MICRO_SERVER_NAME",
   120  			Usage:  "Name of the server. go.micro.srv.example",
   121  		},
   122  		cli.StringFlag{
   123  			Name:   "server_version",
   124  			EnvVar: "MICRO_SERVER_VERSION",
   125  			Usage:  "Version of the server. 1.1.0",
   126  		},
   127  		cli.StringFlag{
   128  			Name:   "server_id",
   129  			EnvVar: "MICRO_SERVER_ID",
   130  			Usage:  "Id of the server. Auto-generated if not specified",
   131  		},
   132  		cli.StringFlag{
   133  			Name:   "server_address",
   134  			EnvVar: "MICRO_SERVER_ADDRESS",
   135  			Usage:  "Bind address for the server. 127.0.0.1:8080",
   136  		},
   137  		cli.StringFlag{
   138  			Name:   "server_advertise",
   139  			EnvVar: "MICRO_SERVER_ADVERTISE",
   140  			Usage:  "Used instead of the server_address when registering with discovery. 127.0.0.1:8080",
   141  		},
   142  		cli.StringSliceFlag{
   143  			Name:   "server_metadata",
   144  			EnvVar: "MICRO_SERVER_METADATA",
   145  			Value:  &cli.StringSlice{},
   146  			Usage:  "A list of key-value pairs defining metadata. version=1.0.0",
   147  		},
   148  		cli.StringFlag{
   149  			Name:   "broker",
   150  			EnvVar: "MICRO_BROKER",
   151  			Usage:  "Broker for pub/sub. http, nats, rabbitmq",
   152  		},
   153  		cli.StringFlag{
   154  			Name:   "broker_address",
   155  			EnvVar: "MICRO_BROKER_ADDRESS",
   156  			Usage:  "Comma-separated list of broker addresses",
   157  		},
   158  		cli.StringFlag{
   159  			Name:   "profile",
   160  			Usage:  "Debug profiler for cpu and memory stats",
   161  			EnvVar: "MICRO_DEBUG_PROFILE",
   162  		},
   163  		cli.StringFlag{
   164  			Name:   "registry",
   165  			EnvVar: "MICRO_REGISTRY",
   166  			Usage:  "Registry for discovery. etcd, mdns",
   167  		},
   168  		cli.StringFlag{
   169  			Name:   "registry_address",
   170  			EnvVar: "MICRO_REGISTRY_ADDRESS",
   171  			Usage:  "Comma-separated list of registry addresses",
   172  		},
   173  		cli.StringFlag{
   174  			Name:   "runtime",
   175  			Usage:  "Runtime for building and running services e.g local, kubernetes",
   176  			EnvVar: "MICRO_RUNTIME",
   177  			Value:  "local",
   178  		},
   179  		cli.StringFlag{
   180  			Name:   "selector",
   181  			EnvVar: "MICRO_SELECTOR",
   182  			Usage:  "Selector used to pick nodes for querying",
   183  		},
   184  		cli.StringFlag{
   185  			Name:   "transport",
   186  			EnvVar: "MICRO_TRANSPORT",
   187  			Usage:  "Transport mechanism used; http",
   188  		},
   189  		cli.StringFlag{
   190  			Name:   "transport_address",
   191  			EnvVar: "MICRO_TRANSPORT_ADDRESS",
   192  			Usage:  "Comma-separated list of transport addresses",
   193  		},
   194  	}
   195  
   196  	DefaultBrokers = map[string]func(...broker.Option) broker.Broker{
   197  		"go.micro.broker": brokerSrv.NewBroker,
   198  		"service":         brokerSrv.NewBroker,
   199  		"http":            http.NewBroker,
   200  		"memory":          memory.NewBroker,
   201  		"nats":            nats.NewBroker,
   202  	}
   203  
   204  	DefaultClients = map[string]func(...client.Option) client.Client{
   205  		"rpc":  client.NewClient,
   206  		"mucp": cmucp.NewClient,
   207  		"grpc": cgrpc.NewClient,
   208  	}
   209  
   210  	DefaultRegistries = map[string]func(...registry.Option) registry.Registry{
   211  		"go.micro.registry": regSrv.NewRegistry,
   212  		"service":           regSrv.NewRegistry,
   213  		"etcd":              etcd.NewRegistry,
   214  		"mdns":              mdns.NewRegistry,
   215  		"memory":            rmem.NewRegistry,
   216  	}
   217  
   218  	DefaultSelectors = map[string]func(...selector.Option) selector.Selector{
   219  		"default": selector.NewSelector,
   220  		"dns":     dns.NewSelector,
   221  		"cache":   selector.NewSelector,
   222  		"router":  router.NewSelector,
   223  		"static":  static.NewSelector,
   224  	}
   225  
   226  	DefaultServers = map[string]func(...server.Option) server.Server{
   227  		"rpc":  server.NewServer,
   228  		"mucp": smucp.NewServer,
   229  		"grpc": sgrpc.NewServer,
   230  	}
   231  
   232  	DefaultTransports = map[string]func(...transport.Option) transport.Transport{
   233  		"memory": tmem.NewTransport,
   234  		"http":   thttp.NewTransport,
   235  		"grpc":   tgrpc.NewTransport,
   236  		"quic":   quic.NewTransport,
   237  	}
   238  
   239  	DefaultRuntimes = map[string]func(...runtime.Option) runtime.Runtime{
   240  		"local":      runtime.NewRuntime,
   241  		"kubernetes": kubernetes.NewRuntime,
   242  	}
   243  
   244  	// used for default selection as the fall back
   245  	defaultClient    = "rpc"
   246  	defaultServer    = "rpc"
   247  	defaultBroker    = "http"
   248  	defaultRegistry  = "mdns"
   249  	defaultSelector  = "registry"
   250  	defaultTransport = "http"
   251  	defaultRuntime   = "local"
   252  )
   253  
   254  func init() {
   255  	rand.Seed(time.Now().Unix())
   256  	help := cli.HelpPrinter
   257  	cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) {
   258  		help(writer, templ, data)
   259  		os.Exit(0)
   260  	}
   261  }
   262  
   263  func newCmd(opts ...Option) Cmd {
   264  	options := Options{
   265  		Broker:    &broker.DefaultBroker,
   266  		Client:    &client.DefaultClient,
   267  		Registry:  &registry.DefaultRegistry,
   268  		Server:    &server.DefaultServer,
   269  		Selector:  &selector.DefaultSelector,
   270  		Transport: &transport.DefaultTransport,
   271  		Runtime:   &runtime.DefaultRuntime,
   272  
   273  		Brokers:    DefaultBrokers,
   274  		Clients:    DefaultClients,
   275  		Registries: DefaultRegistries,
   276  		Selectors:  DefaultSelectors,
   277  		Servers:    DefaultServers,
   278  		Transports: DefaultTransports,
   279  		Runtimes:   DefaultRuntimes,
   280  	}
   281  
   282  	for _, o := range opts {
   283  		o(&options)
   284  	}
   285  
   286  	if len(options.Description) == 0 {
   287  		options.Description = "a go-micro service"
   288  	}
   289  
   290  	cmd := new(cmd)
   291  	cmd.opts = options
   292  	cmd.app = cli.NewApp()
   293  	cmd.app.Name = cmd.opts.Name
   294  	cmd.app.Version = cmd.opts.Version
   295  	cmd.app.Usage = cmd.opts.Description
   296  	cmd.app.Before = cmd.Before
   297  	cmd.app.Flags = DefaultFlags
   298  	cmd.app.Action = func(c *cli.Context) {}
   299  
   300  	if len(options.Version) == 0 {
   301  		cmd.app.HideVersion = true
   302  	}
   303  
   304  	return cmd
   305  }
   306  
   307  func (c *cmd) App() *cli.App {
   308  	return c.app
   309  }
   310  
   311  func (c *cmd) Options() Options {
   312  	return c.opts
   313  }
   314  
   315  func (c *cmd) Before(ctx *cli.Context) error {
   316  	// If flags are set then use them otherwise do nothing
   317  	var serverOpts []server.Option
   318  	var clientOpts []client.Option
   319  
   320  	// Set the runtime
   321  	if name := ctx.String("runtime"); len(name) > 0 {
   322  		r, ok := c.opts.Runtimes[name]
   323  		if !ok {
   324  			return fmt.Errorf("Unsupported runtime: %s", name)
   325  		}
   326  
   327  		*c.opts.Runtime = r()
   328  	}
   329  
   330  	// Set the client
   331  	if name := ctx.String("client"); len(name) > 0 {
   332  		// only change if we have the client and type differs
   333  		if cl, ok := c.opts.Clients[name]; ok && (*c.opts.Client).String() != name {
   334  			*c.opts.Client = cl()
   335  		}
   336  	}
   337  
   338  	// Set the server
   339  	if name := ctx.String("server"); len(name) > 0 {
   340  		// only change if we have the server and type differs
   341  		if s, ok := c.opts.Servers[name]; ok && (*c.opts.Server).String() != name {
   342  			*c.opts.Server = s()
   343  		}
   344  	}
   345  
   346  	// Set the broker
   347  	if name := ctx.String("broker"); len(name) > 0 && (*c.opts.Broker).String() != name {
   348  		b, ok := c.opts.Brokers[name]
   349  		if !ok {
   350  			return fmt.Errorf("Broker %s not found", name)
   351  		}
   352  
   353  		*c.opts.Broker = b()
   354  		serverOpts = append(serverOpts, server.Broker(*c.opts.Broker))
   355  		clientOpts = append(clientOpts, client.Broker(*c.opts.Broker))
   356  	}
   357  
   358  	// Set the registry
   359  	if name := ctx.String("registry"); len(name) > 0 && (*c.opts.Registry).String() != name {
   360  		r, ok := c.opts.Registries[name]
   361  		if !ok {
   362  			return fmt.Errorf("Registry %s not found", name)
   363  		}
   364  
   365  		*c.opts.Registry = r()
   366  		serverOpts = append(serverOpts, server.Registry(*c.opts.Registry))
   367  		clientOpts = append(clientOpts, client.Registry(*c.opts.Registry))
   368  
   369  		if err := (*c.opts.Selector).Init(selector.Registry(*c.opts.Registry)); err != nil {
   370  			log.Fatalf("Error configuring registry: %v", err)
   371  		}
   372  
   373  		clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
   374  
   375  		if err := (*c.opts.Broker).Init(broker.Registry(*c.opts.Registry)); err != nil {
   376  			log.Fatalf("Error configuring broker: %v", err)
   377  		}
   378  	}
   379  
   380  	// Set the selector
   381  	if name := ctx.String("selector"); len(name) > 0 && (*c.opts.Selector).String() != name {
   382  		s, ok := c.opts.Selectors[name]
   383  		if !ok {
   384  			return fmt.Errorf("Selector %s not found", name)
   385  		}
   386  
   387  		*c.opts.Selector = s(selector.Registry(*c.opts.Registry))
   388  
   389  		// No server option here. Should there be?
   390  		clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
   391  	}
   392  
   393  	// Set the transport
   394  	if name := ctx.String("transport"); len(name) > 0 && (*c.opts.Transport).String() != name {
   395  		t, ok := c.opts.Transports[name]
   396  		if !ok {
   397  			return fmt.Errorf("Transport %s not found", name)
   398  		}
   399  
   400  		*c.opts.Transport = t()
   401  		serverOpts = append(serverOpts, server.Transport(*c.opts.Transport))
   402  		clientOpts = append(clientOpts, client.Transport(*c.opts.Transport))
   403  	}
   404  
   405  	// Parse the server options
   406  	metadata := make(map[string]string)
   407  	for _, d := range ctx.StringSlice("server_metadata") {
   408  		var key, val string
   409  		parts := strings.Split(d, "=")
   410  		key = parts[0]
   411  		if len(parts) > 1 {
   412  			val = strings.Join(parts[1:], "=")
   413  		}
   414  		metadata[key] = val
   415  	}
   416  
   417  	if len(metadata) > 0 {
   418  		serverOpts = append(serverOpts, server.Metadata(metadata))
   419  	}
   420  
   421  	if len(ctx.String("broker_address")) > 0 {
   422  		if err := (*c.opts.Broker).Init(broker.Addrs(strings.Split(ctx.String("broker_address"), ",")...)); err != nil {
   423  			log.Fatalf("Error configuring broker: %v", err)
   424  		}
   425  	}
   426  
   427  	if len(ctx.String("registry_address")) > 0 {
   428  		if err := (*c.opts.Registry).Init(registry.Addrs(strings.Split(ctx.String("registry_address"), ",")...)); err != nil {
   429  			log.Fatalf("Error configuring registry: %v", err)
   430  		}
   431  	}
   432  
   433  	if len(ctx.String("transport_address")) > 0 {
   434  		if err := (*c.opts.Transport).Init(transport.Addrs(strings.Split(ctx.String("transport_address"), ",")...)); err != nil {
   435  			log.Fatalf("Error configuring transport: %v", err)
   436  		}
   437  	}
   438  
   439  	if len(ctx.String("server_name")) > 0 {
   440  		serverOpts = append(serverOpts, server.Name(ctx.String("server_name")))
   441  	}
   442  
   443  	if len(ctx.String("server_version")) > 0 {
   444  		serverOpts = append(serverOpts, server.Version(ctx.String("server_version")))
   445  	}
   446  
   447  	if len(ctx.String("server_id")) > 0 {
   448  		serverOpts = append(serverOpts, server.Id(ctx.String("server_id")))
   449  	}
   450  
   451  	if len(ctx.String("server_address")) > 0 {
   452  		serverOpts = append(serverOpts, server.Address(ctx.String("server_address")))
   453  	}
   454  
   455  	if len(ctx.String("server_advertise")) > 0 {
   456  		serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
   457  	}
   458  
   459  	if ttl := time.Duration(ctx.GlobalInt("register_ttl")); ttl >= 0 {
   460  		serverOpts = append(serverOpts, server.RegisterTTL(ttl*time.Second))
   461  	}
   462  
   463  	if val := time.Duration(ctx.GlobalInt("register_interval")); val >= 0 {
   464  		serverOpts = append(serverOpts, server.RegisterInterval(val*time.Second))
   465  	}
   466  
   467  	// client opts
   468  	if r := ctx.Int("client_retries"); r >= 0 {
   469  		clientOpts = append(clientOpts, client.Retries(r))
   470  	}
   471  
   472  	if t := ctx.String("client_request_timeout"); len(t) > 0 {
   473  		d, err := time.ParseDuration(t)
   474  		if err != nil {
   475  			return fmt.Errorf("failed to parse client_request_timeout: %v", t)
   476  		}
   477  		clientOpts = append(clientOpts, client.RequestTimeout(d))
   478  	}
   479  
   480  	if r := ctx.Int("client_pool_size"); r > 0 {
   481  		clientOpts = append(clientOpts, client.PoolSize(r))
   482  	}
   483  
   484  	if t := ctx.String("client_pool_ttl"); len(t) > 0 {
   485  		d, err := time.ParseDuration(t)
   486  		if err != nil {
   487  			return fmt.Errorf("failed to parse client_pool_ttl: %v", t)
   488  		}
   489  		clientOpts = append(clientOpts, client.PoolTTL(d))
   490  	}
   491  
   492  	// We have some command line opts for the server.
   493  	// Lets set it up
   494  	if len(serverOpts) > 0 {
   495  		if err := (*c.opts.Server).Init(serverOpts...); err != nil {
   496  			log.Fatalf("Error configuring server: %v", err)
   497  		}
   498  	}
   499  
   500  	// Use an init option?
   501  	if len(clientOpts) > 0 {
   502  		if err := (*c.opts.Client).Init(clientOpts...); err != nil {
   503  			log.Fatalf("Error configuring client: %v", err)
   504  		}
   505  	}
   506  
   507  	return nil
   508  }
   509  
   510  func (c *cmd) Init(opts ...Option) error {
   511  	for _, o := range opts {
   512  		o(&c.opts)
   513  	}
   514  	c.app.Name = c.opts.Name
   515  	c.app.Version = c.opts.Version
   516  	c.app.HideVersion = len(c.opts.Version) == 0
   517  	c.app.Usage = c.opts.Description
   518  	c.app.RunAndExitOnError()
   519  	return nil
   520  }
   521  
   522  func DefaultOptions() Options {
   523  	return DefaultCmd.Options()
   524  }
   525  
   526  func App() *cli.App {
   527  	return DefaultCmd.App()
   528  }
   529  
   530  func Init(opts ...Option) error {
   531  	return DefaultCmd.Init(opts...)
   532  }
   533  
   534  func NewCmd(opts ...Option) Cmd {
   535  	return newCmd(opts...)
   536  }