chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-micro.dev/v6/broker"
|
||||
)
|
||||
|
||||
// brokerCommand exposes the broker interface: publish, subscribe.
|
||||
func brokerCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "broker",
|
||||
Usage: "Publish and subscribe to broker topics",
|
||||
Description: `Interact with the message broker.
|
||||
|
||||
micro broker publish <topic> <message> Publish a message to a topic
|
||||
micro broker subscribe <topic> Stream messages from a topic`,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "publish",
|
||||
Usage: "Publish a message to a topic",
|
||||
ArgsUsage: "<topic> <message>",
|
||||
Action: brokerPublish,
|
||||
},
|
||||
{
|
||||
Name: "subscribe",
|
||||
Usage: "Stream messages from a topic",
|
||||
ArgsUsage: "<topic>",
|
||||
Action: brokerSubscribe,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func brokerPublish(c *cli.Context) error {
|
||||
topic := c.Args().Get(0)
|
||||
msg := c.Args().Get(1)
|
||||
if topic == "" || msg == "" {
|
||||
return fail("usage: micro broker publish <topic> <message>")
|
||||
}
|
||||
|
||||
b := broker.DefaultBroker
|
||||
if err := b.Connect(); err != nil {
|
||||
return fail("broker connect: %v", err)
|
||||
}
|
||||
|
||||
if err := b.Publish(topic, &broker.Message{Body: []byte(msg)}); err != nil {
|
||||
return fail("publish: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Published to %q\n", topic)
|
||||
return nil
|
||||
}
|
||||
|
||||
func brokerSubscribe(c *cli.Context) error {
|
||||
topic := c.Args().First()
|
||||
if topic == "" {
|
||||
return fail("usage: micro broker subscribe <topic>")
|
||||
}
|
||||
|
||||
b := broker.DefaultBroker
|
||||
if err := b.Connect(); err != nil {
|
||||
return fail("broker connect: %v", err)
|
||||
}
|
||||
|
||||
sub, err := b.Subscribe(topic, func(e broker.Event) error {
|
||||
fmt.Printf("%s\n", string(e.Message().Body))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fail("subscribe: %v", err)
|
||||
}
|
||||
defer func() { _ = sub.Unsubscribe() }()
|
||||
|
||||
fmt.Printf("Subscribed to %q (Ctrl-C to stop)...\n", topic)
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sig
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-micro.dev/v6/config"
|
||||
"go-micro.dev/v6/config/source/env"
|
||||
)
|
||||
|
||||
// configCommand exposes the config interface: get, dump.
|
||||
//
|
||||
// The CLI loads configuration from environment variables (the source
|
||||
// that makes sense without a running service). Keys use dot notation,
|
||||
// e.g. "database.host" reads from DATABASE_HOST.
|
||||
func configCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "config",
|
||||
Usage: "Read dynamic configuration (from environment)",
|
||||
Description: `Read dynamic configuration loaded from environment variables.
|
||||
|
||||
Keys use dot notation: "database.host" maps to DATABASE_HOST.
|
||||
|
||||
micro config get <key> Read a config value
|
||||
micro config dump Print the full config as JSON`,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Read a config value",
|
||||
ArgsUsage: "<key>",
|
||||
Action: configGet,
|
||||
},
|
||||
{
|
||||
Name: "dump",
|
||||
Usage: "Print the full config",
|
||||
Action: configDump,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig() (config.Config, error) {
|
||||
conf, err := config.NewConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := conf.Load(env.NewSource()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func configGet(c *cli.Context) error {
|
||||
key := c.Args().First()
|
||||
if key == "" {
|
||||
return fail("usage: micro config get <key>")
|
||||
}
|
||||
conf, err := loadConfig()
|
||||
if err != nil {
|
||||
return fail("load config: %v", err)
|
||||
}
|
||||
path := strings.Split(key, ".")
|
||||
val, err := conf.Get(path...)
|
||||
if err != nil {
|
||||
return fail("get %q: %v", key, err)
|
||||
}
|
||||
fmt.Println(string(val.Bytes()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func configDump(c *cli.Context) error {
|
||||
conf, err := loadConfig()
|
||||
if err != nil {
|
||||
return fail("load config: %v", err)
|
||||
}
|
||||
fmt.Println(string(conf.Bytes()))
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-micro.dev/v6/registry"
|
||||
)
|
||||
|
||||
// registryCommand exposes the registry interface: list, get, watch.
|
||||
func registryCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "registry",
|
||||
Usage: "Inspect the service registry",
|
||||
Description: `Interact with the service registry.
|
||||
|
||||
micro registry list List all registered services
|
||||
micro registry get <name> Show nodes and endpoints for a service
|
||||
micro registry watch Stream registration events`,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "List all registered services",
|
||||
Action: registryList,
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Show details for a service",
|
||||
ArgsUsage: "<name>",
|
||||
Action: registryGet,
|
||||
},
|
||||
{
|
||||
Name: "watch",
|
||||
Usage: "Stream registration events",
|
||||
Action: registryWatch,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func registryList(c *cli.Context) error {
|
||||
services, err := registry.ListServices()
|
||||
if err != nil {
|
||||
return fail("list services: %v", err)
|
||||
}
|
||||
out := make([]map[string]any, 0, len(services))
|
||||
for _, s := range services {
|
||||
out = append(out, map[string]any{
|
||||
"name": s.Name,
|
||||
"version": s.Version,
|
||||
})
|
||||
}
|
||||
return printJSON(out)
|
||||
}
|
||||
|
||||
func registryGet(c *cli.Context) error {
|
||||
name := c.Args().First()
|
||||
if name == "" {
|
||||
return fail("usage: micro registry get <name>")
|
||||
}
|
||||
services, err := registry.GetService(name)
|
||||
if err != nil {
|
||||
return fail("get service %q: %v", name, err)
|
||||
}
|
||||
if len(services) == 0 {
|
||||
return fail("service %q not found", name)
|
||||
}
|
||||
return printJSON(services)
|
||||
}
|
||||
|
||||
func registryWatch(c *cli.Context) error {
|
||||
w, err := registry.Watch()
|
||||
if err != nil {
|
||||
return fail("watch registry: %v", err)
|
||||
}
|
||||
defer w.Stop()
|
||||
|
||||
fmt.Println("Watching registry for changes (Ctrl-C to stop)...")
|
||||
for {
|
||||
res, err := w.Next()
|
||||
if err != nil {
|
||||
return fail("watch: %v", err)
|
||||
}
|
||||
name := ""
|
||||
version := ""
|
||||
if res.Service != nil {
|
||||
name = res.Service.Name
|
||||
version = res.Service.Version
|
||||
}
|
||||
fmt.Printf("%-10s %s %s\n", res.Action, name, version)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Package resource provides CLI commands that map directly onto
|
||||
// go-micro's core interfaces — registry, broker, store, and config.
|
||||
//
|
||||
// Each interface gets its own top-level command with verbs that mirror
|
||||
// the interface methods, so the framework's building blocks are
|
||||
// inspectable and manipulable from the terminal:
|
||||
//
|
||||
// micro registry list
|
||||
// micro broker publish <topic> <message>
|
||||
// micro store read <key>
|
||||
// micro config get <key>
|
||||
//
|
||||
// New resource commands are registered by appending to the commands
|
||||
// slice in init — see registry.go, broker.go, store.go, config.go for
|
||||
// the per-interface implementations.
|
||||
package resource
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-micro.dev/v6/cmd"
|
||||
)
|
||||
|
||||
// commandFunc returns a cli.Command for a single core interface. Add a
|
||||
// new one here to expose another package on the CLI.
|
||||
var commandFuncs = []func() *cli.Command{
|
||||
registryCommand,
|
||||
brokerCommand,
|
||||
storeCommand,
|
||||
configCommand,
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, fn := range commandFuncs {
|
||||
cmd.Register(fn())
|
||||
}
|
||||
}
|
||||
|
||||
// printJSON writes v as indented JSON to stdout.
|
||||
func printJSON(v any) error {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(v)
|
||||
}
|
||||
|
||||
// fail returns a cli error with a consistent prefix.
|
||||
func fail(format string, args ...any) error {
|
||||
return cli.Exit(fmt.Sprintf(format, args...), 1)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package resource
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCommandsRegistered(t *testing.T) {
|
||||
// Each command func must return a command with a name and at least
|
||||
// one subcommand, so the resource surface stays consistent.
|
||||
for _, fn := range commandFuncs {
|
||||
c := fn()
|
||||
if c.Name == "" {
|
||||
t.Error("command with empty name")
|
||||
}
|
||||
if len(c.Subcommands) == 0 {
|
||||
t.Errorf("command %q has no subcommands", c.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpectedCommands(t *testing.T) {
|
||||
names := map[string]bool{}
|
||||
for _, fn := range commandFuncs {
|
||||
names[fn().Name] = true
|
||||
}
|
||||
for _, want := range []string{"registry", "broker", "store", "config"} {
|
||||
if !names[want] {
|
||||
t.Errorf("missing %q command", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go-micro.dev/v6/store"
|
||||
)
|
||||
|
||||
// storeCommand exposes the store interface: read, write, delete, list.
|
||||
func storeCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "store",
|
||||
Usage: "Read and write records in the store",
|
||||
Description: `Interact with the data store.
|
||||
|
||||
micro store list [prefix] List keys (optionally by prefix)
|
||||
micro store read <key> Read a record
|
||||
micro store write <key> <value> Write a record
|
||||
micro store delete <key> Delete a record`,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "List keys",
|
||||
ArgsUsage: "[prefix]",
|
||||
Action: storeList,
|
||||
},
|
||||
{
|
||||
Name: "read",
|
||||
Usage: "Read a record",
|
||||
ArgsUsage: "<key>",
|
||||
Action: storeRead,
|
||||
},
|
||||
{
|
||||
Name: "write",
|
||||
Usage: "Write a record",
|
||||
ArgsUsage: "<key> <value>",
|
||||
Action: storeWrite,
|
||||
},
|
||||
{
|
||||
Name: "delete",
|
||||
Usage: "Delete a record",
|
||||
ArgsUsage: "<key>",
|
||||
Action: storeDelete,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func storeList(c *cli.Context) error {
|
||||
var opts []store.ListOption
|
||||
if prefix := c.Args().First(); prefix != "" {
|
||||
opts = append(opts, store.ListPrefix(prefix))
|
||||
}
|
||||
keys, err := store.DefaultStore.List(opts...)
|
||||
if err != nil {
|
||||
return fail("list: %v", err)
|
||||
}
|
||||
return printJSON(keys)
|
||||
}
|
||||
|
||||
func storeRead(c *cli.Context) error {
|
||||
key := c.Args().First()
|
||||
if key == "" {
|
||||
return fail("usage: micro store read <key>")
|
||||
}
|
||||
records, err := store.DefaultStore.Read(key)
|
||||
if err != nil {
|
||||
return fail("read %q: %v", key, err)
|
||||
}
|
||||
if len(records) == 0 {
|
||||
return fail("key %q not found", key)
|
||||
}
|
||||
// Print the raw value for a single record, JSON for multiple.
|
||||
if len(records) == 1 {
|
||||
fmt.Println(string(records[0].Value))
|
||||
return nil
|
||||
}
|
||||
return printJSON(records)
|
||||
}
|
||||
|
||||
func storeWrite(c *cli.Context) error {
|
||||
key := c.Args().Get(0)
|
||||
value := c.Args().Get(1)
|
||||
if key == "" {
|
||||
return fail("usage: micro store write <key> <value>")
|
||||
}
|
||||
rec := &store.Record{Key: key, Value: []byte(value)}
|
||||
if err := store.DefaultStore.Write(rec); err != nil {
|
||||
return fail("write %q: %v", key, err)
|
||||
}
|
||||
fmt.Printf("Wrote %q\n", key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func storeDelete(c *cli.Context) error {
|
||||
key := c.Args().First()
|
||||
if key == "" {
|
||||
return fail("usage: micro store delete <key>")
|
||||
}
|
||||
if err := store.DefaultStore.Delete(key); err != nil {
|
||||
return fail("delete %q: %v", key, err)
|
||||
}
|
||||
fmt.Printf("Deleted %q\n", key)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user