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