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: ®istry.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 }