Compare commits
177 Commits
v3.0.0-alp
...
v3.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
|
87e898f4fc | ||
|
d246ccbeef | ||
|
017e156440 | ||
|
d8f17ac827 | ||
|
6e2c9e7cd4 | ||
|
4028e0156b | ||
|
76275e857c | ||
|
a18806c9ef | ||
|
255fecb4f4 | ||
|
af8d55eb7f | ||
|
354a169050 | ||
|
6e083b9aca | ||
|
9dbd75f2cc | ||
|
8975184b88 | ||
|
19a54f2970 | ||
|
3c7f663e8b | ||
|
8fdc8f05ce | ||
|
35349bd313 | ||
|
d5bfa1e795 | ||
|
3bb76868d1 | ||
|
275e92be32 | ||
|
d2728b498c | ||
|
601b223cfb | ||
|
04d2aa4696 | ||
|
b8f79a3fc6 | ||
|
acd3bea0c6 | ||
|
a02a25d955 | ||
|
d5e345d41d | ||
|
71f8cbd5e2 | ||
|
f12473f4b1 | ||
|
724e2b5830 | ||
|
6bdf33c4ee | ||
|
84f52fd7ac | ||
|
6e30b53280 | ||
|
a60426c884 | ||
|
2998735bf3 | ||
|
3a96135df8 | ||
|
bf8b3aeac7 | ||
|
5a52b5929c | ||
|
0adb469a85 | ||
|
21004341bf | ||
|
cc26f2b8b1 | ||
|
1a6652fe6b | ||
|
d28f0670d6 | ||
|
7bdd619e1b | ||
|
c62d1d5eb8 | ||
|
d60d85de5c | ||
|
44f281f8d9 | ||
|
f698feac9c | ||
|
f55701b374 | ||
|
82e8298b73 | ||
|
fc54503232 | ||
|
6f0594eebe | ||
|
6b52f859cf | ||
|
a3d4b8f79b | ||
|
7c7df6b35d | ||
|
e80eab397a | ||
|
6cda6ef92e | ||
|
f9f61d29de | ||
|
1ae825032c | ||
|
f146b52418 | ||
|
78a79ca9e1 | ||
|
329bc2f265 | ||
|
8738ed7757 | ||
|
29e8cdbfe9 | ||
|
47f356fc5f | ||
|
21ffc73c4f | ||
|
81a9342b83 | ||
|
66df1bb361 | ||
|
7eaec450a1 | ||
|
5d6b7b3d7d | ||
|
2eac8ed64f | ||
|
2b2dc2f811 | ||
|
21cca297c0 | ||
|
19ef225b2f | ||
2a23224d91 | |||
|
dd2dc7a2b9 | ||
|
4413372a3f | ||
|
a2a808f2d6 | ||
|
7a6669d199 | ||
|
09fdd3c121 | ||
|
da4159513e | ||
|
e1248f90f4 | ||
|
3011bad518 | ||
|
bb7fe21c46 | ||
|
4fd4a116f2 | ||
|
50ec6c748f | ||
|
7135787b78 | ||
|
870a1ebc07 | ||
|
61899398b3 | ||
|
55d62fc1a5 | ||
|
5238a8a85f | ||
|
9fffd0419e | ||
|
58794df27c | ||
|
5a88ea7247 | ||
|
374aae1490 | ||
|
ccf2f4efd6 | ||
|
9380b365de | ||
|
f0142febcf | ||
|
1fa3ac5599 | ||
|
375b67ee16 | ||
|
69a53e8070 | ||
|
b6e1c7ac99 | ||
|
e83a808b05 | ||
|
012ec6a998 | ||
|
fae4151027 | ||
|
e162e6d505 | ||
|
c51ef6fc29 | ||
|
28d6340f04 | ||
|
4fc193f95d | ||
|
d072eb6ff2 | ||
|
1263806a39 | ||
|
959407bad9 | ||
|
61d12d3a39 | ||
|
4db8ea8f6a | ||
|
13f495587e | ||
|
593b543230 | ||
|
fdce953c15 | ||
|
96836f2e43 | ||
|
65e6ee8566 | ||
|
a7c70c66b1 | ||
|
b2582c0992 | ||
|
6373cc91b7 | ||
|
ed704640aa | ||
|
cd9e5a1e9e | ||
|
dcf040ec9f | ||
|
f838c33008 | ||
|
e8ea0f85e9 | ||
|
51f8b4ae3d | ||
|
69a2032dd7 | ||
|
64feb6dff2 | ||
|
4c95c65d81 | ||
|
4469a41ae7 | ||
|
fc67593ee4 | ||
|
e7cc3c2210 | ||
|
712fe39a62 | ||
|
9b14eb8aec | ||
|
124b1bd7b7 | ||
|
ac1aace214 | ||
|
324c4e6886 | ||
|
d69a4a30cd | ||
|
a6d7b1d710 | ||
|
8ee31b94a1 | ||
|
37cc7fda92 | ||
|
d61cbd29db | ||
|
b6ab124d83 | ||
|
835343d6a5 | ||
|
74907987d1 | ||
|
fb8533b74e | ||
|
dcf785677f | ||
|
991cdba91d | ||
|
aefd052dd7 | ||
|
2b79910ad9 | ||
|
8674dc8e62 | ||
|
b93cd0c964 | ||
|
39bd6a6ced | ||
|
03d47afe47 | ||
|
38ec233350 | ||
|
eee91ed976 | ||
|
07fef9fd33 | ||
|
1106f1d996 | ||
|
8e126e4fc1 | ||
|
1439b101ec | ||
|
24e5b2a034 | ||
|
971a962894 | ||
|
31ed4aa0e8 | ||
|
d2cea4b7b7 | ||
|
0b73d411ad | ||
|
83a64797fb | ||
|
e9fc5b1671 | ||
|
006bbefaf3 | ||
|
3d1ba914fc | ||
|
d66803a136 | ||
|
9813f98c8b | ||
c6163bb22f | |||
|
cb4a2864da | ||
|
f17e4fdb44 |
9
.github/workflows/pr.yml
vendored
9
.github/workflows/pr.yml
vendored
@@ -25,4 +25,11 @@ jobs:
|
||||
id: tests
|
||||
env:
|
||||
IN_TRAVIS_CI: yes
|
||||
run: go test -v ./...
|
||||
S3_BLOB_STORE_REGION: ${{ secrets.SCALEWAY_REGION }}
|
||||
S3_BLOB_STORE_ENDPOINT: ${{ secrets.SCALEWAY_ENDPOINT }}
|
||||
S3_BLOB_STORE_ACCESS_KEY: ${{ secrets.SCALEWAY_ACCESS_KEY }}
|
||||
S3_BLOB_STORE_SECRET_KEY: ${{ secrets.SCALEWAY_SECRET_KEY }}
|
||||
run: |
|
||||
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar xvz
|
||||
cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure &
|
||||
go test -v ./...
|
||||
|
43
.github/workflows/tests.yml
vendored
43
.github/workflows/tests.yml
vendored
@@ -7,13 +7,17 @@ jobs:
|
||||
name: Test repo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.13
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
id: go
|
||||
|
||||
- name: Setup Kind
|
||||
uses: engineerd/setup-kind@v0.4.0
|
||||
with:
|
||||
version: v0.8.1
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -25,27 +29,20 @@ jobs:
|
||||
id: tests
|
||||
env:
|
||||
IN_TRAVIS_CI: yes
|
||||
run: go test -v ./...
|
||||
S3_BLOB_STORE_REGION: ${{ secrets.SCALEWAY_REGION }}
|
||||
S3_BLOB_STORE_ENDPOINT: ${{ secrets.SCALEWAY_ENDPOINT }}
|
||||
S3_BLOB_STORE_ACCESS_KEY: ${{ secrets.SCALEWAY_ACCESS_KEY }}
|
||||
S3_BLOB_STORE_SECRET_KEY: ${{ secrets.SCALEWAY_SECRET_KEY }}
|
||||
run: |
|
||||
kubectl apply -f runtime/kubernetes/test/test.yaml
|
||||
sudo mkdir -p /var/run/secrets/kubernetes.io/serviceaccount
|
||||
sudo chmod 777 /var/run/secrets/kubernetes.io/serviceaccount
|
||||
wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.4.linux-amd64.tgz | tar -xvz
|
||||
cockroach-v20.1.4.linux-amd64/cockroach start-single-node --insecure &
|
||||
wget -q https://github.com/nats-io/nats-streaming-server/releases/download/v0.18.0/nats-streaming-server-v0.18.0-linux-amd64.zip
|
||||
unzip ./nats-streaming-server-v0.18.0-linux-amd64.zip
|
||||
export PATH=$PATH:./nats-streaming-server-v0.18.0-linux-amd64
|
||||
nats-streaming-server &
|
||||
go test -tags kubernetes,nats -v ./...
|
||||
|
||||
- name: Notify of test failure
|
||||
if: failure()
|
||||
uses: rtCamp/action-slack-notify@v2.0.0
|
||||
env:
|
||||
SLACK_CHANNEL: build
|
||||
SLACK_COLOR: '#BF280A'
|
||||
SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
|
||||
SLACK_TITLE: Tests Failed
|
||||
SLACK_USERNAME: GitHub Actions
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
- name: Notify of test success
|
||||
if: success()
|
||||
uses: rtCamp/action-slack-notify@v2.0.0
|
||||
env:
|
||||
SLACK_CHANNEL: build
|
||||
SLACK_COLOR: '#1FAD2B'
|
||||
SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
|
||||
SLACK_TITLE: Tests Passed
|
||||
SLACK_USERNAME: GitHub Actions
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Go Micro [](https://opensource.org/licenses/Apache-2.0) [](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) [](https://travis-ci.org/micro/go-micro) [](https://goreportcard.com/report/github.com/micro/go-micro)
|
||||
# Go Micro [](https://opensource.org/licenses/Apache-2.0) [](https://pkg.go.dev/github.com/micro/go-micro/v3?tab=overview) [](https://travis-ci.org/micro/go-micro) [](https://goreportcard.com/report/github.com/micro/go-micro)
|
||||
|
||||
Go Micro is a standard library for distributed systems development.
|
||||
Go Micro is a standard library for microservices.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -48,7 +48,7 @@ are pluggable and allows Go Micro to be runtime agnostic. You can plugin any und
|
||||
|
||||
## Getting Started
|
||||
|
||||
See [pkg.go.dev](https://pkg.go.dev/github.com/micro/go-micro?tab=doc) for usage.
|
||||
See [pkg.go.dev](https://pkg.go.dev/github.com/micro/go-micro/v3?tab=overview) for usage.
|
||||
|
||||
## License
|
||||
|
||||
|
@@ -1,2 +0,0 @@
|
||||
// Package agent provides an interface for building robots
|
||||
package agent
|
@@ -1,54 +0,0 @@
|
||||
// Package command is an interface for defining bot commands
|
||||
package command
|
||||
|
||||
var (
|
||||
// Commands keyed by golang/regexp patterns
|
||||
// regexp.Match(key, input) is used to match
|
||||
Commands = map[string]Command{}
|
||||
)
|
||||
|
||||
// Command is the interface for specific named
|
||||
// commands executed via plugins or the bot.
|
||||
type Command interface {
|
||||
// Executes the command with args passed in
|
||||
Exec(args ...string) ([]byte, error)
|
||||
// Usage of the command
|
||||
Usage() string
|
||||
// Description of the command
|
||||
Description() string
|
||||
// Name of the command
|
||||
String() string
|
||||
}
|
||||
|
||||
type cmd struct {
|
||||
name string
|
||||
usage string
|
||||
description string
|
||||
exec func(args ...string) ([]byte, error)
|
||||
}
|
||||
|
||||
func (c *cmd) Description() string {
|
||||
return c.description
|
||||
}
|
||||
|
||||
func (c *cmd) Exec(args ...string) ([]byte, error) {
|
||||
return c.exec(args...)
|
||||
}
|
||||
|
||||
func (c *cmd) Usage() string {
|
||||
return c.usage
|
||||
}
|
||||
|
||||
func (c *cmd) String() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
// NewCommand helps quickly create a new command
|
||||
func NewCommand(name, usage, description string, exec func(args ...string) ([]byte, error)) Command {
|
||||
return &cmd{
|
||||
name: name,
|
||||
usage: usage,
|
||||
description: description,
|
||||
exec: exec,
|
||||
}
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommand(t *testing.T) {
|
||||
c := &cmd{
|
||||
name: "test",
|
||||
usage: "test usage",
|
||||
description: "test description",
|
||||
exec: func(args ...string) ([]byte, error) {
|
||||
return []byte("test"), nil
|
||||
},
|
||||
}
|
||||
|
||||
if c.String() != c.name {
|
||||
t.Fatalf("expected name %s got %s", c.name, c.String())
|
||||
}
|
||||
|
||||
if c.Usage() != c.usage {
|
||||
t.Fatalf("expected usage %s got %s", c.usage, c.Usage())
|
||||
}
|
||||
|
||||
if c.Description() != c.description {
|
||||
t.Fatalf("expected description %s got %s", c.description, c.Description())
|
||||
}
|
||||
|
||||
if r, err := c.Exec(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(r) != "test" {
|
||||
t.Fatalf("expected exec result test got %s", string(r))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCommand(t *testing.T) {
|
||||
c := &cmd{
|
||||
name: "test",
|
||||
usage: "test usage",
|
||||
description: "test description",
|
||||
exec: func(args ...string) ([]byte, error) {
|
||||
return []byte("test"), nil
|
||||
},
|
||||
}
|
||||
|
||||
nc := NewCommand(c.name, c.usage, c.description, c.exec)
|
||||
|
||||
if nc.String() != c.name {
|
||||
t.Fatalf("expected name %s got %s", c.name, nc.String())
|
||||
}
|
||||
|
||||
if nc.Usage() != c.usage {
|
||||
t.Fatalf("expected usage %s got %s", c.usage, nc.Usage())
|
||||
}
|
||||
|
||||
if nc.Description() != c.description {
|
||||
t.Fatalf("expected description %s got %s", c.description, nc.Description())
|
||||
}
|
||||
|
||||
if r, err := nc.Exec(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(r) != "test" {
|
||||
t.Fatalf("expected exec result test got %s", string(r))
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
# Discord input for micro-bot
|
||||
[Discord](https://discordapp.com) support for micro bot based on [discordgo](github.com/bwmarrin/discordgo).
|
||||
|
||||
This was originally written by Aleksandr Tihomirov (@zet4) and can be found at https://github.com/zet4/micro-misc/.
|
||||
|
||||
## Options
|
||||
### discord_token
|
||||
|
||||
You have to supply an application token via `--discord_token`.
|
||||
|
||||
Head over to Discord's [developer introduction](https://discordapp.com/developers/docs/intro)
|
||||
to learn how to create applications and how the API works.
|
||||
|
||||
### discord_prefix
|
||||
|
||||
Set a command prefix with `--discord_prefix`. The default prefix is `Micro `.
|
||||
You can mention the bot or use the prefix to run a command.
|
||||
|
||||
### discord_whitelist
|
||||
|
||||
Pass a list of comma-separated user IDs with `--discord_whitelist`. Only allow
|
||||
these users to use the bot.
|
@@ -1,116 +0,0 @@
|
||||
package discord
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/micro/go-micro/v3/agent/input"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
)
|
||||
|
||||
type discordConn struct {
|
||||
master *discordInput
|
||||
exit chan struct{}
|
||||
recv chan *discordgo.Message
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func newConn(master *discordInput) *discordConn {
|
||||
conn := &discordConn{
|
||||
master: master,
|
||||
exit: make(chan struct{}),
|
||||
recv: make(chan *discordgo.Message),
|
||||
}
|
||||
|
||||
conn.master.session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
if m.Author.ID == master.botID {
|
||||
return
|
||||
}
|
||||
|
||||
whitelisted := false
|
||||
for _, ID := range conn.master.whitelist {
|
||||
if m.Author.ID == ID {
|
||||
whitelisted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(master.whitelist) > 0 && !whitelisted {
|
||||
return
|
||||
}
|
||||
|
||||
var valid bool
|
||||
m.Message.Content, valid = conn.master.prefixfn(m.Message.Content)
|
||||
if !valid {
|
||||
return
|
||||
}
|
||||
|
||||
conn.recv <- m.Message
|
||||
})
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func (dc *discordConn) Recv(event *input.Event) error {
|
||||
for {
|
||||
select {
|
||||
case <-dc.exit:
|
||||
return errors.New("connection closed")
|
||||
case msg := <-dc.recv:
|
||||
|
||||
event.From = msg.ChannelID + ":" + msg.Author.ID
|
||||
event.To = dc.master.botID
|
||||
event.Type = input.TextEvent
|
||||
event.Data = []byte(msg.Content)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ChunkString(s string, chunkSize int) []string {
|
||||
var chunks []string
|
||||
runes := []rune(s)
|
||||
|
||||
if len(runes) == 0 {
|
||||
return []string{s}
|
||||
}
|
||||
|
||||
for i := 0; i < len(runes); i += chunkSize {
|
||||
nn := i + chunkSize
|
||||
if nn > len(runes) {
|
||||
nn = len(runes)
|
||||
}
|
||||
chunks = append(chunks, string(runes[i:nn]))
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
func (dc *discordConn) Send(e *input.Event) error {
|
||||
fields := strings.Split(e.To, ":")
|
||||
for _, chunk := range ChunkString(string(e.Data), 2000) {
|
||||
_, err := dc.master.session.ChannelMessageSend(fields[0], chunk)
|
||||
if err != nil {
|
||||
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
|
||||
logger.Error("[bot][loop][send]", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *discordConn) Close() error {
|
||||
if err := dc.master.session.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-dc.exit:
|
||||
return nil
|
||||
default:
|
||||
close(dc.exit)
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -1,153 +0,0 @@
|
||||
package discord
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v3/agent/input"
|
||||
)
|
||||
|
||||
func init() {
|
||||
input.Inputs["discord"] = newInput()
|
||||
}
|
||||
|
||||
func newInput() *discordInput {
|
||||
return &discordInput{}
|
||||
}
|
||||
|
||||
type discordInput struct {
|
||||
token string
|
||||
whitelist []string
|
||||
prefix string
|
||||
prefixfn func(string) (string, bool)
|
||||
botID string
|
||||
|
||||
session *discordgo.Session
|
||||
|
||||
sync.Mutex
|
||||
running bool
|
||||
exit chan struct{}
|
||||
}
|
||||
|
||||
func (d *discordInput) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "discord_token",
|
||||
EnvVars: []string{"MICRO_DISCORD_TOKEN"},
|
||||
Usage: "Discord token (prefix with Bot if it's for bot account)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "discord_whitelist",
|
||||
EnvVars: []string{"MICRO_DISCORD_WHITELIST"},
|
||||
Usage: "Discord Whitelist (seperated by ,)",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "discord_prefix",
|
||||
Usage: "Discord Prefix",
|
||||
EnvVars: []string{"MICRO_DISCORD_PREFIX"},
|
||||
Value: "Micro ",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *discordInput) Init(ctx *cli.Context) error {
|
||||
token := ctx.String("discord_token")
|
||||
whitelist := ctx.String("discord_whitelist")
|
||||
prefix := ctx.String("discord_prefix")
|
||||
|
||||
if len(token) == 0 {
|
||||
return errors.New("require token")
|
||||
}
|
||||
|
||||
d.token = token
|
||||
d.prefix = prefix
|
||||
|
||||
if len(whitelist) > 0 {
|
||||
d.whitelist = strings.Split(whitelist, ",")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *discordInput) Start() error {
|
||||
if len(d.token) == 0 {
|
||||
return errors.New("missing discord configuration")
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
if d.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
d.session, err = discordgo.New("Bot " + d.token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u, err := d.session.User("@me")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.botID = u.ID
|
||||
d.prefixfn = CheckPrefixFactory(fmt.Sprintf("<@%s> ", d.botID), fmt.Sprintf("<@!%s> ", d.botID), d.prefix)
|
||||
|
||||
d.exit = make(chan struct{})
|
||||
d.running = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *discordInput) Stream() (input.Conn, error) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
if !d.running {
|
||||
return nil, errors.New("not running")
|
||||
}
|
||||
|
||||
//Fire-n-forget close just in case...
|
||||
d.session.Close()
|
||||
|
||||
conn := newConn(d)
|
||||
if err := d.session.Open(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (d *discordInput) Stop() error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
if !d.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
close(d.exit)
|
||||
d.running = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *discordInput) String() string {
|
||||
return "discord"
|
||||
}
|
||||
|
||||
// CheckPrefixFactory Creates a prefix checking function and stuff.
|
||||
func CheckPrefixFactory(prefixes ...string) func(string) (string, bool) {
|
||||
return func(content string) (string, bool) {
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(content, prefix) {
|
||||
return strings.TrimPrefix(content, prefix), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
}
|
@@ -1,55 +0,0 @@
|
||||
// Package input is an interface for bot inputs
|
||||
package input
|
||||
|
||||
import (
|
||||
"github.com/micro/cli/v2"
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
||||
const (
|
||||
TextEvent EventType = "text"
|
||||
)
|
||||
|
||||
var (
|
||||
// Inputs keyed by name
|
||||
// Example slack or hipchat
|
||||
Inputs = map[string]Input{}
|
||||
)
|
||||
|
||||
// Event is the unit sent and received
|
||||
type Event struct {
|
||||
Type EventType
|
||||
From string
|
||||
To string
|
||||
Data []byte
|
||||
Meta map[string]interface{}
|
||||
}
|
||||
|
||||
// Input is an interface for sources which
|
||||
// provide a way to communicate with the bot.
|
||||
// Slack, HipChat, XMPP, etc.
|
||||
type Input interface {
|
||||
// Provide cli flags
|
||||
Flags() []cli.Flag
|
||||
// Initialise input using cli context
|
||||
Init(*cli.Context) error
|
||||
// Stream events from the input
|
||||
Stream() (Conn, error)
|
||||
// Start the input
|
||||
Start() error
|
||||
// Stop the input
|
||||
Stop() error
|
||||
// name of the input
|
||||
String() string
|
||||
}
|
||||
|
||||
// Conn interface provides a way to
|
||||
// send and receive events. Send and
|
||||
// Recv both block until succeeding
|
||||
// or failing.
|
||||
type Conn interface {
|
||||
Close() error
|
||||
Recv(*Event) error
|
||||
Send(*Event) error
|
||||
}
|
@@ -1,160 +0,0 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/agent/input"
|
||||
"github.com/nlopes/slack"
|
||||
)
|
||||
|
||||
// Satisfies the input.Conn interface
|
||||
type slackConn struct {
|
||||
auth *slack.AuthTestResponse
|
||||
rtm *slack.RTM
|
||||
exit chan bool
|
||||
|
||||
sync.Mutex
|
||||
names map[string]string
|
||||
}
|
||||
|
||||
func (s *slackConn) run() {
|
||||
// func retrieves user names and maps to IDs
|
||||
setNames := func() {
|
||||
names := make(map[string]string)
|
||||
users, err := s.rtm.Client.GetUsers()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
names[user.ID] = user.Name
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
s.names = names
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
setNames()
|
||||
|
||||
t := time.NewTicker(time.Minute)
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.exit:
|
||||
return
|
||||
case <-t.C:
|
||||
setNames()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *slackConn) getName(id string) string {
|
||||
s.Lock()
|
||||
name := s.names[id]
|
||||
s.Unlock()
|
||||
return name
|
||||
}
|
||||
|
||||
func (s *slackConn) Close() error {
|
||||
select {
|
||||
case <-s.exit:
|
||||
return nil
|
||||
default:
|
||||
close(s.exit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *slackConn) Recv(event *input.Event) error {
|
||||
if event == nil {
|
||||
return errors.New("event cannot be nil")
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.exit:
|
||||
return errors.New("connection closed")
|
||||
case e := <-s.rtm.IncomingEvents:
|
||||
switch ev := e.Data.(type) {
|
||||
case *slack.MessageEvent:
|
||||
// only accept type message
|
||||
if ev.Type != "message" {
|
||||
continue
|
||||
}
|
||||
|
||||
// only accept DMs or messages to me
|
||||
switch {
|
||||
case strings.HasPrefix(ev.Channel, "D"):
|
||||
case strings.HasPrefix(ev.Text, s.auth.User):
|
||||
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// Strip username from text
|
||||
switch {
|
||||
case strings.HasPrefix(ev.Text, s.auth.User):
|
||||
args := strings.Split(ev.Text, " ")[1:]
|
||||
ev.Text = strings.Join(args, " ")
|
||||
event.To = s.auth.User
|
||||
case strings.HasPrefix(ev.Text, fmt.Sprintf("<@%s>", s.auth.UserID)):
|
||||
args := strings.Split(ev.Text, " ")[1:]
|
||||
ev.Text = strings.Join(args, " ")
|
||||
event.To = s.auth.UserID
|
||||
}
|
||||
|
||||
if event.Meta == nil {
|
||||
event.Meta = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// fill in the blanks
|
||||
event.From = ev.Channel + ":" + ev.User
|
||||
event.Type = input.TextEvent
|
||||
event.Data = []byte(ev.Text)
|
||||
event.Meta["reply"] = ev
|
||||
return nil
|
||||
case *slack.InvalidAuthEvent:
|
||||
return errors.New("invalid credentials")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *slackConn) Send(event *input.Event) error {
|
||||
var channel, message, name string
|
||||
|
||||
if len(event.To) == 0 {
|
||||
return errors.New("require Event.To")
|
||||
}
|
||||
|
||||
parts := strings.Split(event.To, ":")
|
||||
|
||||
if len(parts) == 2 {
|
||||
channel = parts[0]
|
||||
name = s.getName(parts[1])
|
||||
// try using reply meta
|
||||
} else if ev, ok := event.Meta["reply"]; ok {
|
||||
channel = ev.(*slack.MessageEvent).Channel
|
||||
name = s.getName(ev.(*slack.MessageEvent).User)
|
||||
}
|
||||
|
||||
// don't know where to send the message
|
||||
if len(channel) == 0 {
|
||||
return errors.New("could not determine who message is to")
|
||||
}
|
||||
|
||||
if len(name) == 0 || strings.HasPrefix(channel, "D") {
|
||||
message = string(event.Data)
|
||||
} else {
|
||||
message = fmt.Sprintf("@%s: %s", name, string(event.Data))
|
||||
}
|
||||
|
||||
s.rtm.SendMessage(s.rtm.NewOutgoingMessage(message, channel))
|
||||
return nil
|
||||
}
|
@@ -1,147 +0,0 @@
|
||||
package slack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v3/agent/input"
|
||||
"github.com/nlopes/slack"
|
||||
)
|
||||
|
||||
type slackInput struct {
|
||||
debug bool
|
||||
token string
|
||||
|
||||
sync.Mutex
|
||||
running bool
|
||||
exit chan bool
|
||||
|
||||
api *slack.Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.Inputs["slack"] = NewInput()
|
||||
}
|
||||
|
||||
func (p *slackInput) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "slack_debug",
|
||||
Usage: "Slack debug output",
|
||||
EnvVars: []string{"MICRO_SLACK_DEBUG"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "slack_token",
|
||||
Usage: "Slack token",
|
||||
EnvVars: []string{"MICRO_SLACK_TOKEN"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *slackInput) Init(ctx *cli.Context) error {
|
||||
debug := ctx.Bool("slack_debug")
|
||||
token := ctx.String("slack_token")
|
||||
|
||||
if len(token) == 0 {
|
||||
return errors.New("missing slack token")
|
||||
}
|
||||
|
||||
p.debug = debug
|
||||
p.token = token
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *slackInput) Stream() (input.Conn, error) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if !p.running {
|
||||
return nil, errors.New("not running")
|
||||
}
|
||||
|
||||
// test auth
|
||||
auth, err := p.api.AuthTest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rtm := p.api.NewRTM()
|
||||
exit := make(chan bool)
|
||||
|
||||
go rtm.ManageConnection()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-p.exit:
|
||||
select {
|
||||
case <-exit:
|
||||
return
|
||||
default:
|
||||
close(exit)
|
||||
}
|
||||
case <-exit:
|
||||
}
|
||||
|
||||
rtm.Disconnect()
|
||||
}()
|
||||
|
||||
conn := &slackConn{
|
||||
auth: auth,
|
||||
rtm: rtm,
|
||||
exit: exit,
|
||||
names: make(map[string]string),
|
||||
}
|
||||
|
||||
go conn.run()
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (p *slackInput) Start() error {
|
||||
if len(p.token) == 0 {
|
||||
return errors.New("missing slack token")
|
||||
}
|
||||
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if p.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
api := slack.New(p.token, slack.OptionDebug(p.debug))
|
||||
|
||||
// test auth
|
||||
_, err := api.AuthTest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.api = api
|
||||
p.exit = make(chan bool)
|
||||
p.running = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *slackInput) Stop() error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if !p.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
close(p.exit)
|
||||
p.running = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *slackInput) String() string {
|
||||
return "slack"
|
||||
}
|
||||
|
||||
func NewInput() input.Input {
|
||||
return &slackInput{}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
# Telegram Messenger input for micro bot
|
||||
[Telegram](https://telegram.org) support for micro bot based on [telegram-bot-api](https://github.com/go-telegram-bot-api/telegram-bot-api).
|
||||
|
||||
## Options
|
||||
### --telegram_token (required)
|
||||
|
||||
Sets bot's token for interacting with API.
|
||||
|
||||
Head over to Telegram's [API documentation](https://core.telegram.org/bots/api)
|
||||
to learn how to create bots and how the API works.
|
||||
|
||||
### --telegram_debug
|
||||
|
||||
Sets the debug flag to make the bot's output verbose.
|
||||
|
||||
### --telegram_whitelist
|
||||
|
||||
Sets a list of comma-separated nicknames (without @ symbol in the beginning) for interacting with bot. Only these users can use the bot.
|
@@ -1,115 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/forestgiant/sliceutil"
|
||||
"github.com/micro/go-micro/v3/agent/input"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
tgbotapi "gopkg.in/telegram-bot-api.v4"
|
||||
)
|
||||
|
||||
type telegramConn struct {
|
||||
input *telegramInput
|
||||
|
||||
recv <-chan tgbotapi.Update
|
||||
exit chan bool
|
||||
|
||||
syncCond *sync.Cond
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func newConn(input *telegramInput) (*telegramConn, error) {
|
||||
conn := &telegramConn{
|
||||
input: input,
|
||||
}
|
||||
|
||||
conn.syncCond = sync.NewCond(&conn.mutex)
|
||||
|
||||
go conn.run()
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (tc *telegramConn) run() {
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
updates, err := tc.input.api.GetUpdatesChan(u)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tc.recv = updates
|
||||
tc.syncCond.Signal()
|
||||
|
||||
select {
|
||||
case <-tc.exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *telegramConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *telegramConn) Recv(event *input.Event) error {
|
||||
if event == nil {
|
||||
return errors.New("event cannot be nil")
|
||||
}
|
||||
|
||||
for {
|
||||
if tc.recv == nil {
|
||||
tc.mutex.Lock()
|
||||
tc.syncCond.Wait()
|
||||
}
|
||||
|
||||
update := <-tc.recv
|
||||
|
||||
if update.Message == nil || (len(tc.input.whitelist) > 0 && !sliceutil.Contains(tc.input.whitelist, update.Message.From.UserName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if event.Meta == nil {
|
||||
event.Meta = make(map[string]interface{})
|
||||
}
|
||||
|
||||
event.Type = input.TextEvent
|
||||
event.From = update.Message.From.UserName
|
||||
event.To = tc.input.api.Self.UserName
|
||||
event.Data = []byte(update.Message.Text)
|
||||
event.Meta["chatId"] = update.Message.Chat.ID
|
||||
event.Meta["chatType"] = update.Message.Chat.Type
|
||||
event.Meta["messageId"] = update.Message.MessageID
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *telegramConn) Send(event *input.Event) error {
|
||||
messageText := strings.TrimSpace(string(event.Data))
|
||||
|
||||
chatId := event.Meta["chatId"].(int64)
|
||||
chatType := ChatType(event.Meta["chatType"].(string))
|
||||
|
||||
msgConfig := tgbotapi.NewMessage(chatId, messageText)
|
||||
msgConfig.ParseMode = tgbotapi.ModeHTML
|
||||
|
||||
if sliceutil.Contains([]ChatType{Group, Supergroup}, chatType) {
|
||||
msgConfig.ReplyToMessageID = event.Meta["messageId"].(int)
|
||||
}
|
||||
|
||||
_, err := tc.input.api.Send(msgConfig)
|
||||
|
||||
if err != nil {
|
||||
// probably it could be because of nested HTML tags -- telegram doesn't allow nested tags
|
||||
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
|
||||
logger.Error("[telegram][Send] error:", err)
|
||||
}
|
||||
msgConfig.Text = "This bot couldn't send the response (Internal error)"
|
||||
tc.input.api.Send(msgConfig)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v3/agent/input"
|
||||
tgbotapi "gopkg.in/telegram-bot-api.v4"
|
||||
)
|
||||
|
||||
type telegramInput struct {
|
||||
sync.Mutex
|
||||
|
||||
debug bool
|
||||
token string
|
||||
whitelist []string
|
||||
|
||||
api *tgbotapi.BotAPI
|
||||
}
|
||||
|
||||
type ChatType string
|
||||
|
||||
const (
|
||||
Private ChatType = "private"
|
||||
Group ChatType = "group"
|
||||
Supergroup ChatType = "supergroup"
|
||||
)
|
||||
|
||||
func init() {
|
||||
input.Inputs["telegram"] = &telegramInput{}
|
||||
}
|
||||
|
||||
func (ti *telegramInput) Flags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "telegram_debug",
|
||||
EnvVars: []string{"MICRO_TELEGRAM_DEBUG"},
|
||||
Usage: "Telegram debug output",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "telegram_token",
|
||||
EnvVars: []string{"MICRO_TELEGRAM_TOKEN"},
|
||||
Usage: "Telegram token",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "telegram_whitelist",
|
||||
EnvVars: []string{"MICRO_TELEGRAM_WHITELIST"},
|
||||
Usage: "Telegram bot's users (comma-separated values)",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (ti *telegramInput) Init(ctx *cli.Context) error {
|
||||
ti.debug = ctx.Bool("telegram_debug")
|
||||
ti.token = ctx.String("telegram_token")
|
||||
|
||||
whitelist := ctx.String("telegram_whitelist")
|
||||
|
||||
if whitelist != "" {
|
||||
ti.whitelist = strings.Split(whitelist, ",")
|
||||
}
|
||||
|
||||
if len(ti.token) == 0 {
|
||||
return errors.New("missing telegram token")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ti *telegramInput) Stream() (input.Conn, error) {
|
||||
ti.Lock()
|
||||
defer ti.Unlock()
|
||||
|
||||
return newConn(ti)
|
||||
}
|
||||
|
||||
func (ti *telegramInput) Start() error {
|
||||
ti.Lock()
|
||||
defer ti.Unlock()
|
||||
|
||||
api, err := tgbotapi.NewBotAPI(ti.token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ti.api = api
|
||||
|
||||
api.Debug = ti.debug
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ti *telegramInput) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *telegramInput) String() string {
|
||||
return "telegram"
|
||||
}
|
@@ -1,333 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: agent/proto/bot.proto
|
||||
|
||||
package go_micro_bot
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type HelpRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HelpRequest) Reset() { *m = HelpRequest{} }
|
||||
func (m *HelpRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*HelpRequest) ProtoMessage() {}
|
||||
func (*HelpRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_79b974b8c77805fa, []int{0}
|
||||
}
|
||||
|
||||
func (m *HelpRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HelpRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HelpRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpRequest.Merge(m, src)
|
||||
}
|
||||
func (m *HelpRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_HelpRequest.Size(m)
|
||||
}
|
||||
func (m *HelpRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HelpRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HelpRequest proto.InternalMessageInfo
|
||||
|
||||
type HelpResponse struct {
|
||||
Usage string `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"`
|
||||
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HelpResponse) Reset() { *m = HelpResponse{} }
|
||||
func (m *HelpResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*HelpResponse) ProtoMessage() {}
|
||||
func (*HelpResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_79b974b8c77805fa, []int{1}
|
||||
}
|
||||
|
||||
func (m *HelpResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HelpResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HelpResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HelpResponse.Merge(m, src)
|
||||
}
|
||||
func (m *HelpResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_HelpResponse.Size(m)
|
||||
}
|
||||
func (m *HelpResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HelpResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HelpResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *HelpResponse) GetUsage() string {
|
||||
if m != nil {
|
||||
return m.Usage
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *HelpResponse) GetDescription() string {
|
||||
if m != nil {
|
||||
return m.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ExecRequest struct {
|
||||
Args []string `protobuf:"bytes,1,rep,name=args,proto3" json:"args,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ExecRequest) Reset() { *m = ExecRequest{} }
|
||||
func (m *ExecRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExecRequest) ProtoMessage() {}
|
||||
func (*ExecRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_79b974b8c77805fa, []int{2}
|
||||
}
|
||||
|
||||
func (m *ExecRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExecRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExecRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecRequest.Merge(m, src)
|
||||
}
|
||||
func (m *ExecRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ExecRequest.Size(m)
|
||||
}
|
||||
func (m *ExecRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExecRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExecRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *ExecRequest) GetArgs() []string {
|
||||
if m != nil {
|
||||
return m.Args
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ExecResponse struct {
|
||||
Result []byte `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ExecResponse) Reset() { *m = ExecResponse{} }
|
||||
func (m *ExecResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExecResponse) ProtoMessage() {}
|
||||
func (*ExecResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_79b974b8c77805fa, []int{3}
|
||||
}
|
||||
|
||||
func (m *ExecResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ExecResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ExecResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExecResponse.Merge(m, src)
|
||||
}
|
||||
func (m *ExecResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ExecResponse.Size(m)
|
||||
}
|
||||
func (m *ExecResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExecResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExecResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ExecResponse) GetResult() []byte {
|
||||
if m != nil {
|
||||
return m.Result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ExecResponse) GetError() string {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*HelpRequest)(nil), "go.micro.bot.HelpRequest")
|
||||
proto.RegisterType((*HelpResponse)(nil), "go.micro.bot.HelpResponse")
|
||||
proto.RegisterType((*ExecRequest)(nil), "go.micro.bot.ExecRequest")
|
||||
proto.RegisterType((*ExecResponse)(nil), "go.micro.bot.ExecResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("agent/proto/bot.proto", fileDescriptor_79b974b8c77805fa) }
|
||||
|
||||
var fileDescriptor_79b974b8c77805fa = []byte{
|
||||
// 234 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x3f, 0x4f, 0xc3, 0x30,
|
||||
0x10, 0xc5, 0x1b, 0x28, 0x45, 0xbd, 0x84, 0xc5, 0x02, 0x14, 0x3a, 0x05, 0x4f, 0x9d, 0x5c, 0x09,
|
||||
0x56, 0x24, 0x06, 0x04, 0x62, 0xce, 0x37, 0x48, 0xd2, 0x53, 0x14, 0xa9, 0xf1, 0x99, 0xb3, 0x23,
|
||||
0xf1, 0x1d, 0xf8, 0xd2, 0xc8, 0x7f, 0x06, 0xab, 0xea, 0x76, 0xcf, 0x67, 0xbd, 0xf7, 0x7b, 0x07,
|
||||
0x0f, 0xdd, 0x88, 0xda, 0x1d, 0x0c, 0x93, 0xa3, 0x43, 0x4f, 0x4e, 0x85, 0x49, 0x54, 0x23, 0xa9,
|
||||
0x79, 0x1a, 0x98, 0x54, 0x4f, 0x4e, 0xde, 0x41, 0xf9, 0x8d, 0x27, 0xd3, 0xe2, 0xcf, 0x82, 0xd6,
|
||||
0xc9, 0x2f, 0xa8, 0xa2, 0xb4, 0x86, 0xb4, 0x45, 0x71, 0x0f, 0x37, 0x8b, 0xed, 0x46, 0xac, 0x8b,
|
||||
0xa6, 0xd8, 0x6f, 0xdb, 0x28, 0x44, 0x03, 0xe5, 0x11, 0xed, 0xc0, 0x93, 0x71, 0x13, 0xe9, 0xfa,
|
||||
0x2a, 0xec, 0xf2, 0x27, 0xf9, 0x0c, 0xe5, 0xe7, 0x2f, 0x0e, 0xc9, 0x56, 0x08, 0x58, 0x77, 0x3c,
|
||||
0xda, 0xba, 0x68, 0xae, 0xf7, 0xdb, 0x36, 0xcc, 0xf2, 0x0d, 0xaa, 0xf8, 0x25, 0x45, 0x3d, 0xc2,
|
||||
0x86, 0xd1, 0x2e, 0x27, 0x17, 0xb2, 0xaa, 0x36, 0x29, 0x8f, 0x80, 0xcc, 0xc4, 0x29, 0x26, 0x8a,
|
||||
0x97, 0xbf, 0x02, 0x6e, 0x3f, 0x68, 0x9e, 0x3b, 0x7d, 0x14, 0xef, 0xb0, 0xf6, 0xd0, 0xe2, 0x49,
|
||||
0xe5, 0xd5, 0x54, 0xd6, 0x6b, 0xb7, 0xbb, 0xb4, 0x8a, 0xc1, 0x72, 0xe5, 0x0d, 0x3c, 0xca, 0xb9,
|
||||
0x41, 0xd6, 0xe0, 0xdc, 0x20, 0x27, 0x97, 0xab, 0x7e, 0x13, 0x4e, 0xfb, 0xfa, 0x1f, 0x00, 0x00,
|
||||
0xff, 0xff, 0xe8, 0x08, 0x5e, 0xad, 0x73, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// CommandClient is the client API for Command service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type CommandClient interface {
|
||||
Help(ctx context.Context, in *HelpRequest, opts ...grpc.CallOption) (*HelpResponse, error)
|
||||
Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error)
|
||||
}
|
||||
|
||||
type commandClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewCommandClient(cc *grpc.ClientConn) CommandClient {
|
||||
return &commandClient{cc}
|
||||
}
|
||||
|
||||
func (c *commandClient) Help(ctx context.Context, in *HelpRequest, opts ...grpc.CallOption) (*HelpResponse, error) {
|
||||
out := new(HelpResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.bot.Command/Help", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commandClient) Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error) {
|
||||
out := new(ExecResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.bot.Command/Exec", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CommandServer is the server API for Command service.
|
||||
type CommandServer interface {
|
||||
Help(context.Context, *HelpRequest) (*HelpResponse, error)
|
||||
Exec(context.Context, *ExecRequest) (*ExecResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedCommandServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedCommandServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedCommandServer) Help(ctx context.Context, req *HelpRequest) (*HelpResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Help not implemented")
|
||||
}
|
||||
func (*UnimplementedCommandServer) Exec(ctx context.Context, req *ExecRequest) (*ExecResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Exec not implemented")
|
||||
}
|
||||
|
||||
func RegisterCommandServer(s *grpc.Server, srv CommandServer) {
|
||||
s.RegisterService(&_Command_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Command_Help_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HelpRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CommandServer).Help(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.bot.Command/Help",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CommandServer).Help(ctx, req.(*HelpRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Command_Exec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ExecRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CommandServer).Exec(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.bot.Command/Exec",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CommandServer).Exec(ctx, req.(*ExecRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Command_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.bot.Command",
|
||||
HandlerType: (*CommandServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Help",
|
||||
Handler: _Command_Help_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Exec",
|
||||
Handler: _Command_Exec_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "agent/proto/bot.proto",
|
||||
}
|
@@ -1,110 +0,0 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: agent/proto/bot.proto
|
||||
|
||||
package go_micro_bot
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/go-micro/v3/api"
|
||||
client "github.com/micro/go-micro/v3/client"
|
||||
server "github.com/micro/go-micro/v3/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for Command service
|
||||
|
||||
func NewCommandEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Command service
|
||||
|
||||
type CommandService interface {
|
||||
Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error)
|
||||
Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error)
|
||||
}
|
||||
|
||||
type commandService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewCommandService(name string, c client.Client) CommandService {
|
||||
return &commandService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *commandService) Help(ctx context.Context, in *HelpRequest, opts ...client.CallOption) (*HelpResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Command.Help", in)
|
||||
out := new(HelpResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commandService) Exec(ctx context.Context, in *ExecRequest, opts ...client.CallOption) (*ExecResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Command.Exec", in)
|
||||
out := new(ExecResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Command service
|
||||
|
||||
type CommandHandler interface {
|
||||
Help(context.Context, *HelpRequest, *HelpResponse) error
|
||||
Exec(context.Context, *ExecRequest, *ExecResponse) error
|
||||
}
|
||||
|
||||
func RegisterCommandHandler(s server.Server, hdlr CommandHandler, opts ...server.HandlerOption) error {
|
||||
type command interface {
|
||||
Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error
|
||||
Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error
|
||||
}
|
||||
type Command struct {
|
||||
command
|
||||
}
|
||||
h := &commandHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Command{h}, opts...))
|
||||
}
|
||||
|
||||
type commandHandler struct {
|
||||
CommandHandler
|
||||
}
|
||||
|
||||
func (h *commandHandler) Help(ctx context.Context, in *HelpRequest, out *HelpResponse) error {
|
||||
return h.CommandHandler.Help(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commandHandler) Exec(ctx context.Context, in *ExecRequest, out *ExecResponse) error {
|
||||
return h.CommandHandler.Exec(ctx, in, out)
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package go.micro.bot;
|
||||
|
||||
service Command {
|
||||
rpc Help(HelpRequest) returns (HelpResponse) {};
|
||||
rpc Exec(ExecRequest) returns (ExecResponse) {};
|
||||
}
|
||||
|
||||
message HelpRequest {
|
||||
}
|
||||
|
||||
message HelpResponse {
|
||||
string usage = 1;
|
||||
string description = 2;
|
||||
}
|
||||
|
||||
message ExecRequest {
|
||||
repeated string args = 1;
|
||||
}
|
||||
|
||||
message ExecResponse {
|
||||
bytes result = 1;
|
||||
string error = 2;
|
||||
}
|
@@ -6,11 +6,11 @@ import (
|
||||
|
||||
goapi "github.com/micro/go-micro/v3/api"
|
||||
"github.com/micro/go-micro/v3/api/handler"
|
||||
"github.com/micro/go-micro/v3/api/handler/util"
|
||||
api "github.com/micro/go-micro/v3/api/proto"
|
||||
"github.com/micro/go-micro/v3/client"
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/util/ctx"
|
||||
"github.com/micro/go-micro/v3/util/router"
|
||||
)
|
||||
|
||||
type apiHandler struct {
|
||||
@@ -72,7 +72,7 @@ func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// create the context from headers
|
||||
cx := ctx.FromRequest(r)
|
||||
|
||||
if err := c.Call(cx, req, rsp, client.WithRouter(util.Router(service.Services))); err != nil {
|
||||
if err := c.Call(cx, req, rsp, client.WithRouter(router.New(service.Services))); err != nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
ce := errors.Parse(err.Error())
|
||||
switch ce.Code {
|
||||
|
@@ -11,7 +11,6 @@ import (
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
"github.com/micro/go-micro/v3/api"
|
||||
"github.com/micro/go-micro/v3/api/handler"
|
||||
"github.com/micro/go-micro/v3/api/handler/util"
|
||||
"github.com/micro/go-micro/v3/api/internal/proto"
|
||||
"github.com/micro/go-micro/v3/client"
|
||||
"github.com/micro/go-micro/v3/codec"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
"github.com/micro/go-micro/v3/util/ctx"
|
||||
"github.com/micro/go-micro/v3/util/qson"
|
||||
"github.com/micro/go-micro/v3/util/router"
|
||||
"github.com/oxtoacart/bpool"
|
||||
)
|
||||
|
||||
@@ -113,7 +113,7 @@ func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// create custom router
|
||||
callOpt := client.WithRouter(util.Router(service.Services))
|
||||
callOpt := client.WithRouter(router.New(service.Services))
|
||||
|
||||
// walk the standard call path
|
||||
// get payload
|
||||
@@ -249,7 +249,7 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
return raw.Marshal()
|
||||
case strings.Contains(ct, "application/www-x-form-urlencoded"):
|
||||
case strings.Contains(ct, "application/x-www-form-urlencoded"):
|
||||
r.ParseForm()
|
||||
|
||||
// generate a new set of values from the form
|
||||
@@ -364,6 +364,13 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
bodybuf = b
|
||||
}
|
||||
if bodydst == "" || bodydst == "*" {
|
||||
// jsonpatch resequences the json object so we avoid it if possible (some usecases such as
|
||||
// validating signatures require the request body to be unchangedd). We're keeping support
|
||||
// for the custom paramaters for backwards compatability reasons.
|
||||
if string(out) == "{}" {
|
||||
return bodybuf, nil
|
||||
}
|
||||
|
||||
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
|
||||
return out, nil
|
||||
}
|
||||
@@ -410,7 +417,6 @@ func requestPayload(r *http.Request) ([]byte, error) {
|
||||
|
||||
//fallback to previous unknown behaviour
|
||||
return bodybuf, nil
|
||||
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
|
@@ -13,10 +13,10 @@ import (
|
||||
"github.com/gobwas/ws"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
"github.com/micro/go-micro/v3/api"
|
||||
"github.com/micro/go-micro/v3/api/handler/util"
|
||||
"github.com/micro/go-micro/v3/client"
|
||||
raw "github.com/micro/go-micro/v3/codec/bytes"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
"github.com/micro/go-micro/v3/util/router"
|
||||
)
|
||||
|
||||
// serveWebsocket will stream rpc back over websockets assuming json
|
||||
@@ -111,7 +111,7 @@ func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request,
|
||||
)
|
||||
|
||||
// create custom router
|
||||
callOpt := client.WithRouter(util.Router(service.Services))
|
||||
callOpt := client.WithRouter(router.New(service.Services))
|
||||
|
||||
// create a new stream
|
||||
stream, err := c.Stream(ctx, req, callOpt)
|
||||
|
@@ -66,6 +66,11 @@ func (r *Resolver) Domain(req *http.Request) string {
|
||||
// remove the domain from the host, leaving the subdomain, e.g. "staging.foo.myapp.com" => "staging.foo"
|
||||
subdomain := strings.TrimSuffix(host, "."+domain)
|
||||
|
||||
// ignore the API subdomain
|
||||
if subdomain == "api" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// return the reversed subdomain as the namespace, e.g. "staging.foo" => "foo-staging"
|
||||
comps := strings.Split(subdomain, ".")
|
||||
for i := len(comps)/2 - 1; i >= 0; i-- {
|
||||
|
@@ -12,11 +12,11 @@ import (
|
||||
|
||||
"github.com/micro/go-micro/v3/api"
|
||||
"github.com/micro/go-micro/v3/api/router"
|
||||
"github.com/micro/go-micro/v3/api/router/util"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
"github.com/micro/go-micro/v3/registry/cache"
|
||||
util "github.com/micro/go-micro/v3/util/router"
|
||||
)
|
||||
|
||||
// endpoint struct, that holds compiled pcre
|
||||
@@ -127,7 +127,10 @@ func (r *registryRouter) store(services []*registry.Service) {
|
||||
key := fmt.Sprintf("%s.%s", service.Name, sep.Name)
|
||||
// decode endpoint
|
||||
end := api.Decode(sep.Metadata)
|
||||
|
||||
// no endpoint or no name
|
||||
if end == nil || len(end.Name) == 0 {
|
||||
continue
|
||||
}
|
||||
// if we got nothing skip
|
||||
if err := api.Validate(end); err != nil {
|
||||
if logger.V(logger.TraceLevel, logger.DefaultLogger) {
|
||||
|
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/micro/go-micro/v3/client"
|
||||
gcli "github.com/micro/go-micro/v3/client/grpc"
|
||||
rmemory "github.com/micro/go-micro/v3/registry/memory"
|
||||
rt "github.com/micro/go-micro/v3/router"
|
||||
regRouter "github.com/micro/go-micro/v3/router/registry"
|
||||
"github.com/micro/go-micro/v3/server"
|
||||
gsrv "github.com/micro/go-micro/v3/server/grpc"
|
||||
pb "github.com/micro/go-micro/v3/server/grpc/proto"
|
||||
@@ -55,9 +57,13 @@ func initial(t *testing.T) (server.Server, client.Client) {
|
||||
server.Registry(r),
|
||||
)
|
||||
|
||||
rtr := regRouter.NewRouter(
|
||||
rt.Registry(r),
|
||||
)
|
||||
|
||||
// create a new server
|
||||
c := gcli.NewClient(
|
||||
client.Registry(r),
|
||||
client.Router(rtr),
|
||||
)
|
||||
|
||||
h := &testServer{}
|
||||
|
@@ -10,11 +10,11 @@ import (
|
||||
|
||||
"github.com/micro/go-micro/v3/api"
|
||||
"github.com/micro/go-micro/v3/api/router"
|
||||
"github.com/micro/go-micro/v3/api/router/util"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
rutil "github.com/micro/go-micro/v3/util/registry"
|
||||
util "github.com/micro/go-micro/v3/util/router"
|
||||
)
|
||||
|
||||
type endpoint struct {
|
||||
|
@@ -40,5 +40,5 @@ func SetHeaders(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
set(w, "Access-Control-Allow-Credentials", "true")
|
||||
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
|
||||
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Namespace")
|
||||
}
|
||||
|
@@ -23,9 +23,9 @@ type Options struct {
|
||||
|
||||
type Wrapper func(h http.Handler) http.Handler
|
||||
|
||||
func WrapHandler(w Wrapper) Option {
|
||||
func WrapHandler(w ...Wrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Wrappers = append(o.Wrappers, w)
|
||||
o.Wrappers = append(o.Wrappers, w...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,268 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: api/service/proto/api.proto
|
||||
|
||||
package go_micro_api
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Endpoint struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Host []string `protobuf:"bytes,2,rep,name=host,proto3" json:"host,omitempty"`
|
||||
Path []string `protobuf:"bytes,3,rep,name=path,proto3" json:"path,omitempty"`
|
||||
Method []string `protobuf:"bytes,4,rep,name=method,proto3" json:"method,omitempty"`
|
||||
Stream bool `protobuf:"varint,5,opt,name=stream,proto3" json:"stream,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Endpoint) Reset() { *m = Endpoint{} }
|
||||
func (m *Endpoint) String() string { return proto.CompactTextString(m) }
|
||||
func (*Endpoint) ProtoMessage() {}
|
||||
func (*Endpoint) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_c4a48b6b680b5c31, []int{0}
|
||||
}
|
||||
|
||||
func (m *Endpoint) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Endpoint.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Endpoint) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Endpoint.Merge(m, src)
|
||||
}
|
||||
func (m *Endpoint) XXX_Size() int {
|
||||
return xxx_messageInfo_Endpoint.Size(m)
|
||||
}
|
||||
func (m *Endpoint) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Endpoint.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Endpoint proto.InternalMessageInfo
|
||||
|
||||
func (m *Endpoint) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Endpoint) GetHost() []string {
|
||||
if m != nil {
|
||||
return m.Host
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Endpoint) GetPath() []string {
|
||||
if m != nil {
|
||||
return m.Path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Endpoint) GetMethod() []string {
|
||||
if m != nil {
|
||||
return m.Method
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Endpoint) GetStream() bool {
|
||||
if m != nil {
|
||||
return m.Stream
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type EmptyResponse struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
|
||||
func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*EmptyResponse) ProtoMessage() {}
|
||||
func (*EmptyResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_c4a48b6b680b5c31, []int{1}
|
||||
}
|
||||
|
||||
func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_EmptyResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *EmptyResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_EmptyResponse.Merge(m, src)
|
||||
}
|
||||
func (m *EmptyResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_EmptyResponse.Size(m)
|
||||
}
|
||||
func (m *EmptyResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Endpoint)(nil), "go.micro.api.Endpoint")
|
||||
proto.RegisterType((*EmptyResponse)(nil), "go.micro.api.EmptyResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("api/service/proto/api.proto", fileDescriptor_c4a48b6b680b5c31) }
|
||||
|
||||
var fileDescriptor_c4a48b6b680b5c31 = []byte{
|
||||
// 212 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0xd0, 0xc1, 0x4a, 0x03, 0x31,
|
||||
0x10, 0x80, 0x61, 0xd7, 0xad, 0x65, 0x1d, 0x14, 0x21, 0x87, 0x12, 0xec, 0x65, 0xd9, 0x53, 0x4f,
|
||||
0x59, 0xd0, 0x27, 0x28, 0xda, 0x17, 0xd8, 0x37, 0x88, 0xed, 0xd0, 0x9d, 0x43, 0x32, 0x43, 0x32,
|
||||
0x14, 0x7c, 0x08, 0xdf, 0x59, 0x12, 0x2b, 0x2c, 0x5e, 0xbd, 0xfd, 0xf3, 0x1d, 0x86, 0x61, 0x60,
|
||||
0xeb, 0x85, 0xc6, 0x8c, 0xe9, 0x42, 0x47, 0x1c, 0x25, 0xb1, 0xf2, 0xe8, 0x85, 0x5c, 0x2d, 0xf3,
|
||||
0x70, 0x66, 0x17, 0xe8, 0x98, 0xd8, 0x79, 0xa1, 0xe1, 0x02, 0xdd, 0x21, 0x9e, 0x84, 0x29, 0xaa,
|
||||
0x31, 0xb0, 0x8a, 0x3e, 0xa0, 0x6d, 0xfa, 0x66, 0x77, 0x3f, 0xd5, 0x2e, 0x36, 0x73, 0x56, 0x7b,
|
||||
0xdb, 0xb7, 0xc5, 0x4a, 0x17, 0x13, 0xaf, 0xb3, 0x6d, 0x7f, 0xac, 0xb4, 0xd9, 0xc0, 0x3a, 0xa0,
|
||||
0xce, 0x7c, 0xb2, 0xab, 0xaa, 0xd7, 0xa9, 0x78, 0xd6, 0x84, 0x3e, 0xd8, 0xbb, 0xbe, 0xd9, 0x75,
|
||||
0xd3, 0x75, 0x1a, 0x9e, 0xe0, 0xf1, 0x10, 0x44, 0x3f, 0x27, 0xcc, 0xc2, 0x31, 0xe3, 0xcb, 0x57,
|
||||
0x03, 0xed, 0x5e, 0xc8, 0xec, 0xa1, 0x9b, 0xf0, 0x4c, 0x59, 0x31, 0x99, 0x8d, 0x5b, 0xde, 0xea,
|
||||
0x7e, 0x0f, 0x7d, 0xde, 0xfe, 0xf1, 0xe5, 0xa2, 0xe1, 0xc6, 0xbc, 0x01, 0xbc, 0x63, 0xfa, 0xdf,
|
||||
0x92, 0x8f, 0x75, 0xfd, 0xd6, 0xeb, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x46, 0x62, 0x67, 0x30,
|
||||
0x4c, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// ApiClient is the client API for Api service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ApiClient interface {
|
||||
Register(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error)
|
||||
Deregister(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error)
|
||||
}
|
||||
|
||||
type apiClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewApiClient(cc *grpc.ClientConn) ApiClient {
|
||||
return &apiClient{cc}
|
||||
}
|
||||
|
||||
func (c *apiClient) Register(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) {
|
||||
out := new(EmptyResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.api.Api/Register", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *apiClient) Deregister(ctx context.Context, in *Endpoint, opts ...grpc.CallOption) (*EmptyResponse, error) {
|
||||
out := new(EmptyResponse)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.api.Api/Deregister", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ApiServer is the server API for Api service.
|
||||
type ApiServer interface {
|
||||
Register(context.Context, *Endpoint) (*EmptyResponse, error)
|
||||
Deregister(context.Context, *Endpoint) (*EmptyResponse, error)
|
||||
}
|
||||
|
||||
// UnimplementedApiServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedApiServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedApiServer) Register(ctx context.Context, req *Endpoint) (*EmptyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Register not implemented")
|
||||
}
|
||||
func (*UnimplementedApiServer) Deregister(ctx context.Context, req *Endpoint) (*EmptyResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Deregister not implemented")
|
||||
}
|
||||
|
||||
func RegisterApiServer(s *grpc.Server, srv ApiServer) {
|
||||
s.RegisterService(&_Api_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Api_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Endpoint)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ApiServer).Register(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.api.Api/Register",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ApiServer).Register(ctx, req.(*Endpoint))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Api_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Endpoint)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ApiServer).Deregister(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.api.Api/Deregister",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ApiServer).Deregister(ctx, req.(*Endpoint))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Api_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.api.Api",
|
||||
HandlerType: (*ApiServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Register",
|
||||
Handler: _Api_Register_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Deregister",
|
||||
Handler: _Api_Deregister_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "api/service/proto/api.proto",
|
||||
}
|
@@ -1,110 +0,0 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: api/service/proto/api.proto
|
||||
|
||||
package go_micro_api
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/go-micro/v3/api"
|
||||
client "github.com/micro/go-micro/v3/client"
|
||||
server "github.com/micro/go-micro/v3/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for Api service
|
||||
|
||||
func NewApiEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Api service
|
||||
|
||||
type ApiService interface {
|
||||
Register(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error)
|
||||
Deregister(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error)
|
||||
}
|
||||
|
||||
type apiService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewApiService(name string, c client.Client) ApiService {
|
||||
return &apiService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *apiService) Register(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Api.Register", in)
|
||||
out := new(EmptyResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *apiService) Deregister(ctx context.Context, in *Endpoint, opts ...client.CallOption) (*EmptyResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Api.Deregister", in)
|
||||
out := new(EmptyResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Api service
|
||||
|
||||
type ApiHandler interface {
|
||||
Register(context.Context, *Endpoint, *EmptyResponse) error
|
||||
Deregister(context.Context, *Endpoint, *EmptyResponse) error
|
||||
}
|
||||
|
||||
func RegisterApiHandler(s server.Server, hdlr ApiHandler, opts ...server.HandlerOption) error {
|
||||
type api interface {
|
||||
Register(ctx context.Context, in *Endpoint, out *EmptyResponse) error
|
||||
Deregister(ctx context.Context, in *Endpoint, out *EmptyResponse) error
|
||||
}
|
||||
type Api struct {
|
||||
api
|
||||
}
|
||||
h := &apiHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Api{h}, opts...))
|
||||
}
|
||||
|
||||
type apiHandler struct {
|
||||
ApiHandler
|
||||
}
|
||||
|
||||
func (h *apiHandler) Register(ctx context.Context, in *Endpoint, out *EmptyResponse) error {
|
||||
return h.ApiHandler.Register(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *apiHandler) Deregister(ctx context.Context, in *Endpoint, out *EmptyResponse) error {
|
||||
return h.ApiHandler.Deregister(ctx, in, out)
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package go.micro.api;
|
||||
|
||||
service Api {
|
||||
rpc Register(Endpoint) returns (EmptyResponse) {};
|
||||
rpc Deregister(Endpoint) returns (EmptyResponse) {};
|
||||
}
|
||||
|
||||
message Endpoint {
|
||||
string name = 1;
|
||||
repeated string host = 2;
|
||||
repeated string path = 3;
|
||||
repeated string method = 4;
|
||||
bool stream = 5;
|
||||
}
|
||||
|
||||
message EmptyResponse {}
|
23
auth/auth.go
23
auth/auth.go
@@ -2,14 +2,11 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// BearerScheme used for Authorization header
|
||||
BearerScheme = "Bearer "
|
||||
// ScopePublic is the scope applied to a rule to allow access to the public
|
||||
ScopePublic = ""
|
||||
// ScopeAccount is the scope applied to a rule to limit to users with any valid account
|
||||
@@ -49,7 +46,7 @@ type Auth interface {
|
||||
|
||||
// Account provided by an auth provider
|
||||
type Account struct {
|
||||
// ID of the account e.g. email
|
||||
// ID of the account e.g. UUID. Should not change
|
||||
ID string `json:"id"`
|
||||
// Type of the account, e.g. service
|
||||
Type string `json:"type"`
|
||||
@@ -61,6 +58,8 @@ type Account struct {
|
||||
Scopes []string `json:"scopes"`
|
||||
// Secret for the account, e.g. the password
|
||||
Secret string `json:"secret"`
|
||||
// Name of the account. User friendly name that might change e.g. a username or email
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Token can be short or long lived
|
||||
@@ -115,19 +114,3 @@ type Rule struct {
|
||||
// rule will be applied
|
||||
Priority int32
|
||||
}
|
||||
|
||||
type accountKey struct{}
|
||||
|
||||
// AccountFromContext gets the account from the context, which
|
||||
// is set by the auth wrapper at the start of a call. If the account
|
||||
// is not set, a nil account will be returned. The error is only returned
|
||||
// when there was a problem retrieving an account
|
||||
func AccountFromContext(ctx context.Context) (*Account, bool) {
|
||||
acc, ok := ctx.Value(accountKey{}).(*Account)
|
||||
return acc, ok
|
||||
}
|
||||
|
||||
// ContextWithAccount sets the account in the context
|
||||
func ContextWithAccount(ctx context.Context, account *Account) context.Context {
|
||||
return context.WithValue(ctx, accountKey{}, account)
|
||||
}
|
||||
|
@@ -54,13 +54,17 @@ func (j *jwtAuth) Generate(id string, opts ...auth.GenerateOption) (*auth.Accoun
|
||||
if len(options.Issuer) == 0 {
|
||||
options.Issuer = j.Options().Issuer
|
||||
}
|
||||
|
||||
name := options.Name
|
||||
if name == "" {
|
||||
name = id
|
||||
}
|
||||
account := &auth.Account{
|
||||
ID: id,
|
||||
Type: options.Type,
|
||||
Scopes: options.Scopes,
|
||||
Metadata: options.Metadata,
|
||||
Issuer: options.Issuer,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
// generate a JWT secret which can be provided to the Token() method
|
||||
|
@@ -40,13 +40,17 @@ func (n *noop) Options() auth.Options {
|
||||
// Generate a new account
|
||||
func (n *noop) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
|
||||
options := auth.NewGenerateOptions(opts...)
|
||||
|
||||
name := options.Name
|
||||
if name == "" {
|
||||
name = id
|
||||
}
|
||||
return &auth.Account{
|
||||
ID: id,
|
||||
Secret: options.Secret,
|
||||
Metadata: options.Metadata,
|
||||
Scopes: options.Scopes,
|
||||
Issuer: n.Options().Issuer,
|
||||
Name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@@ -110,6 +110,8 @@ type GenerateOptions struct {
|
||||
Secret string
|
||||
// Issuer of the account, e.g. micro
|
||||
Issuer string
|
||||
// Name of the acouunt e.g. an email or username
|
||||
Name string
|
||||
}
|
||||
|
||||
type GenerateOption func(o *GenerateOptions)
|
||||
@@ -156,6 +158,13 @@ func WithIssuer(i string) GenerateOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithName for the generated account
|
||||
func WithName(n string) GenerateOption {
|
||||
return func(o *GenerateOptions) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
||||
// NewGenerateOptions from a slice of options
|
||||
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
|
||||
var options GenerateOptions
|
||||
|
@@ -14,23 +14,15 @@ type Broker interface {
|
||||
}
|
||||
|
||||
// Handler is used to process messages via a subscription of a topic.
|
||||
// The handler is passed a publication interface which contains the
|
||||
// message and optional Ack method to acknowledge receipt of the message.
|
||||
type Handler func(Event) error
|
||||
type Handler func(*Message) error
|
||||
|
||||
type ErrorHandler func(*Message, error)
|
||||
|
||||
type Message struct {
|
||||
Header map[string]string
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// Event is given to a subscription handler for processing
|
||||
type Event interface {
|
||||
Topic() string
|
||||
Message() *Message
|
||||
Ack() error
|
||||
Error() error
|
||||
}
|
||||
|
||||
// Subscriber is a convenience return type for the Subscribe method
|
||||
type Subscriber interface {
|
||||
Options() SubscribeOptions
|
||||
|
@@ -60,12 +60,6 @@ type httpSubscriber struct {
|
||||
hb *httpBroker
|
||||
}
|
||||
|
||||
type httpEvent struct {
|
||||
m *broker.Message
|
||||
t string
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultPath = "/"
|
||||
DefaultAddress = "127.0.0.1:0"
|
||||
@@ -155,22 +149,6 @@ func newHttpBroker(opts ...broker.Option) broker.Broker {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *httpEvent) Ack() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpEvent) Error() error {
|
||||
return h.err
|
||||
}
|
||||
|
||||
func (h *httpEvent) Message() *broker.Message {
|
||||
return h.m
|
||||
}
|
||||
|
||||
func (h *httpEvent) Topic() string {
|
||||
return h.t
|
||||
}
|
||||
|
||||
func (h *httpSubscriber) Options() broker.SubscribeOptions {
|
||||
return h.opts
|
||||
}
|
||||
@@ -310,16 +288,15 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var m *broker.Message
|
||||
if err = h.opts.Codec.Unmarshal(b, &m); err != nil {
|
||||
var msg *broker.Message
|
||||
if err = h.opts.Codec.Unmarshal(b, &msg); err != nil {
|
||||
errr := merr.InternalServerError("go.micro.broker", "Error parsing request body: %v", err)
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(errr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
topic := m.Header["Micro-Topic"]
|
||||
//delete(m.Header, ":topic")
|
||||
topic := msg.Header["Micro-Topic"]
|
||||
|
||||
if len(topic) == 0 {
|
||||
errr := merr.InternalServerError("go.micro.broker", "Topic not found")
|
||||
@@ -328,7 +305,6 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
p := &httpEvent{m: m, t: topic}
|
||||
id := req.Form.Get("id")
|
||||
|
||||
//nolint:prealloc
|
||||
@@ -345,7 +321,7 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// execute the handler
|
||||
for _, fn := range subs {
|
||||
p.err = fn(p)
|
||||
fn(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -83,9 +83,8 @@ func sub(be *testing.B, c int) {
|
||||
done := make(chan bool, c)
|
||||
|
||||
for i := 0; i < c; i++ {
|
||||
sub, err := b.Subscribe(topic, func(p broker.Event) error {
|
||||
sub, err := b.Subscribe(topic, func(m *broker.Message) error {
|
||||
done <- true
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
@@ -140,9 +139,8 @@ func pub(be *testing.B, c int) {
|
||||
|
||||
done := make(chan bool, c*4)
|
||||
|
||||
sub, err := b.Subscribe(topic, func(p broker.Event) error {
|
||||
sub, err := b.Subscribe(topic, func(m *broker.Message) error {
|
||||
done <- true
|
||||
m := p.Message()
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
be.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
@@ -208,8 +206,7 @@ func TestBroker(t *testing.T) {
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
sub, err := b.Subscribe("test", func(p broker.Event) error {
|
||||
m := p.Message()
|
||||
sub, err := b.Subscribe("test", func(m *broker.Message) error {
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
@@ -257,11 +254,9 @@ func TestConcurrentSubBroker(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
sub, err := b.Subscribe("test", func(p broker.Event) error {
|
||||
sub, err := b.Subscribe("test", func(m *broker.Message) error {
|
||||
defer wg.Done()
|
||||
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
@@ -312,11 +307,9 @@ func TestConcurrentPubBroker(t *testing.T) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
sub, err := b.Subscribe("test", func(p broker.Event) error {
|
||||
sub, err := b.Subscribe("test", func(m *broker.Message) error {
|
||||
defer wg.Done()
|
||||
|
||||
m := p.Message()
|
||||
|
||||
if string(m.Body) != string(msg.Body) {
|
||||
t.Fatalf("Unexpected msg %s, expected %s", string(m.Body), string(msg.Body))
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/go-micro/v3/broker"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
maddr "github.com/micro/go-micro/v3/util/addr"
|
||||
mnet "github.com/micro/go-micro/v3/util/net"
|
||||
)
|
||||
@@ -24,13 +23,6 @@ type memoryBroker struct {
|
||||
Subscribers map[string][]*memorySubscriber
|
||||
}
|
||||
|
||||
type memoryEvent struct {
|
||||
opts broker.Options
|
||||
topic string
|
||||
err error
|
||||
message interface{}
|
||||
}
|
||||
|
||||
type memorySubscriber struct {
|
||||
id string
|
||||
topic string
|
||||
@@ -103,31 +95,12 @@ func (m *memoryBroker) Publish(topic string, msg *broker.Message, opts ...broker
|
||||
return nil
|
||||
}
|
||||
|
||||
var v interface{}
|
||||
if m.opts.Codec != nil {
|
||||
buf, err := m.opts.Codec.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v = buf
|
||||
} else {
|
||||
v = msg
|
||||
}
|
||||
|
||||
p := &memoryEvent{
|
||||
topic: topic,
|
||||
message: v,
|
||||
opts: m.opts,
|
||||
}
|
||||
|
||||
for _, sub := range subs {
|
||||
if err := sub.handler(p); err != nil {
|
||||
p.err = err
|
||||
if eh := m.opts.ErrorHandler; eh != nil {
|
||||
eh(p)
|
||||
continue
|
||||
if err := sub.handler(msg); err != nil {
|
||||
if eh := sub.opts.ErrorHandler; eh != nil {
|
||||
eh(msg, err)
|
||||
}
|
||||
return err
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,36 +153,6 @@ func (m *memoryBroker) String() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
func (m *memoryEvent) Topic() string {
|
||||
return m.topic
|
||||
}
|
||||
|
||||
func (m *memoryEvent) Message() *broker.Message {
|
||||
switch v := m.message.(type) {
|
||||
case *broker.Message:
|
||||
return v
|
||||
case []byte:
|
||||
msg := &broker.Message{}
|
||||
if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
|
||||
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
|
||||
logger.Errorf("[memory]: failed to unmarshal: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryEvent) Ack() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryEvent) Error() error {
|
||||
return m.err
|
||||
}
|
||||
|
||||
func (m *memorySubscriber) Options() broker.SubscribeOptions {
|
||||
return m.opts
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ func TestMemoryBroker(t *testing.T) {
|
||||
topic := "test"
|
||||
count := 10
|
||||
|
||||
fn := func(p broker.Event) error {
|
||||
fn := func(m *broker.Message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -1,17 +0,0 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/v3/broker"
|
||||
)
|
||||
|
||||
// setBrokerOption returns a function to setup a context with given value
|
||||
func setBrokerOption(k, v interface{}) broker.Option {
|
||||
return func(o *broker.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, k, v)
|
||||
}
|
||||
}
|
@@ -1,322 +0,0 @@
|
||||
// Package nats provides a NATS broker
|
||||
package nats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/micro/go-micro/v3/broker"
|
||||
"github.com/micro/go-micro/v3/codec/json"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
"github.com/micro/go-micro/v3/registry/mdns"
|
||||
nats "github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type natsBroker struct {
|
||||
sync.Once
|
||||
sync.RWMutex
|
||||
|
||||
// indicate if we're connected
|
||||
connected bool
|
||||
|
||||
addrs []string
|
||||
conn *nats.Conn
|
||||
opts broker.Options
|
||||
nopts nats.Options
|
||||
|
||||
// should we drain the connection
|
||||
drain bool
|
||||
closeCh chan (error)
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
s *nats.Subscription
|
||||
opts broker.SubscribeOptions
|
||||
}
|
||||
|
||||
type publication struct {
|
||||
t string
|
||||
err error
|
||||
m *broker.Message
|
||||
}
|
||||
|
||||
func (p *publication) Topic() string {
|
||||
return p.t
|
||||
}
|
||||
|
||||
func (p *publication) Message() *broker.Message {
|
||||
return p.m
|
||||
}
|
||||
|
||||
func (p *publication) Ack() error {
|
||||
// nats does not support acking
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *publication) Error() error {
|
||||
return p.err
|
||||
}
|
||||
|
||||
func (s *subscriber) Options() broker.SubscribeOptions {
|
||||
return s.opts
|
||||
}
|
||||
|
||||
func (s *subscriber) Topic() string {
|
||||
return s.s.Subject
|
||||
}
|
||||
|
||||
func (s *subscriber) Unsubscribe() error {
|
||||
return s.s.Unsubscribe()
|
||||
}
|
||||
|
||||
func (n *natsBroker) Address() string {
|
||||
if n.conn != nil && n.conn.IsConnected() {
|
||||
return n.conn.ConnectedUrl()
|
||||
}
|
||||
|
||||
if len(n.addrs) > 0 {
|
||||
return n.addrs[0]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n *natsBroker) setAddrs(addrs []string) []string {
|
||||
//nolint:prealloc
|
||||
var cAddrs []string
|
||||
for _, addr := range addrs {
|
||||
if len(addr) == 0 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(addr, "nats://") {
|
||||
addr = "nats://" + addr
|
||||
}
|
||||
cAddrs = append(cAddrs, addr)
|
||||
}
|
||||
if len(cAddrs) == 0 {
|
||||
cAddrs = []string{nats.DefaultURL}
|
||||
}
|
||||
return cAddrs
|
||||
}
|
||||
|
||||
func (n *natsBroker) Connect() error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if n.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
status := nats.CLOSED
|
||||
if n.conn != nil {
|
||||
status = n.conn.Status()
|
||||
}
|
||||
|
||||
switch status {
|
||||
case nats.CONNECTED, nats.RECONNECTING, nats.CONNECTING:
|
||||
n.connected = true
|
||||
return nil
|
||||
default: // DISCONNECTED or CLOSED or DRAINING
|
||||
opts := n.nopts
|
||||
opts.Servers = n.addrs
|
||||
opts.Secure = n.opts.Secure
|
||||
opts.TLSConfig = n.opts.TLSConfig
|
||||
|
||||
// secure might not be set
|
||||
if n.opts.TLSConfig != nil {
|
||||
opts.Secure = true
|
||||
}
|
||||
|
||||
c, err := opts.Connect()
|
||||
if err != nil {
|
||||
if logger.V(logger.WarnLevel, logger.DefaultLogger) {
|
||||
logger.Warnf("Error connecting to broker: %v", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
n.conn = c
|
||||
n.connected = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *natsBroker) Disconnect() error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
// drain the connection if specified
|
||||
if n.drain {
|
||||
n.conn.Drain()
|
||||
n.closeCh <- nil
|
||||
}
|
||||
|
||||
// close the client connection
|
||||
n.conn.Close()
|
||||
|
||||
// set not connected
|
||||
n.connected = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Init(opts ...broker.Option) error {
|
||||
n.setOption(opts...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) Options() broker.Options {
|
||||
return n.opts
|
||||
}
|
||||
|
||||
func (n *natsBroker) Publish(topic string, msg *broker.Message, opts ...broker.PublishOption) error {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
if n.conn == nil {
|
||||
return errors.New("not connected")
|
||||
}
|
||||
|
||||
b, err := n.opts.Codec.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return n.conn.Publish(topic, b)
|
||||
}
|
||||
|
||||
func (n *natsBroker) Subscribe(topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||
n.RLock()
|
||||
if n.conn == nil {
|
||||
n.RUnlock()
|
||||
return nil, errors.New("not connected")
|
||||
}
|
||||
n.RUnlock()
|
||||
|
||||
opt := broker.SubscribeOptions{
|
||||
AutoAck: true,
|
||||
Context: context.Background(),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
fn := func(msg *nats.Msg) {
|
||||
var m broker.Message
|
||||
pub := &publication{t: msg.Subject}
|
||||
eh := n.opts.ErrorHandler
|
||||
err := n.opts.Codec.Unmarshal(msg.Data, &m)
|
||||
pub.err = err
|
||||
pub.m = &m
|
||||
if err != nil {
|
||||
m.Body = msg.Data
|
||||
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
|
||||
logger.Error(err)
|
||||
}
|
||||
if eh != nil {
|
||||
eh(pub)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := handler(pub); err != nil {
|
||||
pub.err = err
|
||||
if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
|
||||
logger.Error(err)
|
||||
}
|
||||
if eh != nil {
|
||||
eh(pub)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sub *nats.Subscription
|
||||
var err error
|
||||
|
||||
n.RLock()
|
||||
if len(opt.Queue) > 0 {
|
||||
sub, err = n.conn.QueueSubscribe(topic, opt.Queue, fn)
|
||||
} else {
|
||||
sub, err = n.conn.Subscribe(topic, fn)
|
||||
}
|
||||
n.RUnlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &subscriber{s: sub, opts: opt}, nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) String() string {
|
||||
return "nats"
|
||||
}
|
||||
|
||||
func (n *natsBroker) setOption(opts ...broker.Option) {
|
||||
for _, o := range opts {
|
||||
o(&n.opts)
|
||||
}
|
||||
|
||||
n.Once.Do(func() {
|
||||
n.nopts = nats.GetDefaultOptions()
|
||||
})
|
||||
|
||||
if nopts, ok := n.opts.Context.Value(optionsKey{}).(nats.Options); ok {
|
||||
n.nopts = nopts
|
||||
}
|
||||
|
||||
// broker.Options have higher priority than nats.Options
|
||||
// only if Addrs, Secure or TLSConfig were not set through a broker.Option
|
||||
// we read them from nats.Option
|
||||
if len(n.opts.Addrs) == 0 {
|
||||
n.opts.Addrs = n.nopts.Servers
|
||||
}
|
||||
|
||||
if !n.opts.Secure {
|
||||
n.opts.Secure = n.nopts.Secure
|
||||
}
|
||||
|
||||
if n.opts.TLSConfig == nil {
|
||||
n.opts.TLSConfig = n.nopts.TLSConfig
|
||||
}
|
||||
n.addrs = n.setAddrs(n.opts.Addrs)
|
||||
|
||||
if n.opts.Context.Value(drainConnectionKey{}) != nil {
|
||||
n.drain = true
|
||||
n.closeCh = make(chan error)
|
||||
n.nopts.ClosedCB = n.onClose
|
||||
n.nopts.AsyncErrorCB = n.onAsyncError
|
||||
n.nopts.DisconnectedErrCB = n.onDisconnectedError
|
||||
}
|
||||
}
|
||||
|
||||
func (n *natsBroker) onClose(conn *nats.Conn) {
|
||||
n.closeCh <- nil
|
||||
}
|
||||
|
||||
func (n *natsBroker) onAsyncError(conn *nats.Conn, sub *nats.Subscription, err error) {
|
||||
// There are kinds of different async error nats might callback, but we are interested
|
||||
// in ErrDrainTimeout only here.
|
||||
if err == nats.ErrDrainTimeout {
|
||||
n.closeCh <- err
|
||||
}
|
||||
}
|
||||
|
||||
func (n *natsBroker) onDisconnectedError(conn *nats.Conn, err error) {
|
||||
n.closeCh <- err
|
||||
}
|
||||
|
||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
||||
options := broker.Options{
|
||||
// Default codec
|
||||
Codec: json.Marshaler{},
|
||||
Context: context.Background(),
|
||||
Registry: mdns.NewRegistry(),
|
||||
}
|
||||
|
||||
n := &natsBroker{
|
||||
opts: options,
|
||||
}
|
||||
n.setOption(opts...)
|
||||
|
||||
return n
|
||||
}
|
@@ -1,98 +0,0 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/v3/broker"
|
||||
nats "github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
var addrTestCases = []struct {
|
||||
name string
|
||||
description string
|
||||
addrs map[string]string // expected address : set address
|
||||
}{
|
||||
{
|
||||
"brokerOpts",
|
||||
"set broker addresses through a broker.Option in constructor",
|
||||
map[string]string{
|
||||
"nats://192.168.10.1:5222": "192.168.10.1:5222",
|
||||
"nats://10.20.10.0:4222": "10.20.10.0:4222"},
|
||||
},
|
||||
{
|
||||
"brokerInit",
|
||||
"set broker addresses through a broker.Option in broker.Init()",
|
||||
map[string]string{
|
||||
"nats://192.168.10.1:5222": "192.168.10.1:5222",
|
||||
"nats://10.20.10.0:4222": "10.20.10.0:4222"},
|
||||
},
|
||||
{
|
||||
"natsOpts",
|
||||
"set broker addresses through the nats.Option in constructor",
|
||||
map[string]string{
|
||||
"nats://192.168.10.1:5222": "192.168.10.1:5222",
|
||||
"nats://10.20.10.0:4222": "10.20.10.0:4222"},
|
||||
},
|
||||
{
|
||||
"default",
|
||||
"check if default Address is set correctly",
|
||||
map[string]string{
|
||||
"nats://127.0.0.1:4222": "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TestInitAddrs tests issue #100. Ensures that if the addrs is set by an option in init it will be used.
|
||||
func TestInitAddrs(t *testing.T) {
|
||||
|
||||
for _, tc := range addrTestCases {
|
||||
t.Run(fmt.Sprintf("%s: %s", tc.name, tc.description), func(t *testing.T) {
|
||||
|
||||
var br broker.Broker
|
||||
var addrs []string
|
||||
|
||||
for _, addr := range tc.addrs {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
|
||||
switch tc.name {
|
||||
case "brokerOpts":
|
||||
// we know that there are just two addrs in the dict
|
||||
br = NewBroker(broker.Addrs(addrs[0], addrs[1]))
|
||||
br.Init()
|
||||
case "brokerInit":
|
||||
br = NewBroker()
|
||||
// we know that there are just two addrs in the dict
|
||||
br.Init(broker.Addrs(addrs[0], addrs[1]))
|
||||
case "natsOpts":
|
||||
nopts := nats.GetDefaultOptions()
|
||||
nopts.Servers = addrs
|
||||
br = NewBroker(Options(nopts))
|
||||
br.Init()
|
||||
case "default":
|
||||
br = NewBroker()
|
||||
br.Init()
|
||||
}
|
||||
|
||||
natsBroker, ok := br.(*natsBroker)
|
||||
if !ok {
|
||||
t.Fatal("Expected broker to be of types *natsBroker")
|
||||
}
|
||||
// check if the same amount of addrs we set has actually been set, default
|
||||
// have only 1 address nats://127.0.0.1:4222 (current nats code) or
|
||||
// nats://localhost:4222 (older code version)
|
||||
if len(natsBroker.addrs) != len(tc.addrs) && tc.name != "default" {
|
||||
t.Errorf("Expected Addr count = %d, Actual Addr count = %d",
|
||||
len(natsBroker.addrs), len(tc.addrs))
|
||||
}
|
||||
|
||||
for _, addr := range natsBroker.addrs {
|
||||
_, ok := tc.addrs[addr]
|
||||
if !ok {
|
||||
t.Errorf("Expected '%s' has not been set", addr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/v3/broker"
|
||||
nats "github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type optionsKey struct{}
|
||||
type drainConnectionKey struct{}
|
||||
|
||||
// Options accepts nats.Options
|
||||
func Options(opts nats.Options) broker.Option {
|
||||
return setBrokerOption(optionsKey{}, opts)
|
||||
}
|
||||
|
||||
// DrainConnection will drain subscription on close
|
||||
func DrainConnection() broker.Option {
|
||||
return setBrokerOption(drainConnectionKey{}, struct{}{})
|
||||
}
|
@@ -13,10 +13,6 @@ type Options struct {
|
||||
Secure bool
|
||||
Codec codec.Marshaler
|
||||
|
||||
// Handler executed when error happens in broker message
|
||||
// processing
|
||||
ErrorHandler Handler
|
||||
|
||||
TLSConfig *tls.Config
|
||||
// Registry used for clustering
|
||||
Registry registry.Registry
|
||||
@@ -32,9 +28,9 @@ type PublishOptions struct {
|
||||
}
|
||||
|
||||
type SubscribeOptions struct {
|
||||
// AutoAck defaults to true. When a handler returns
|
||||
// with a nil error the message is acked.
|
||||
AutoAck bool
|
||||
// Handler executed when errors occur processing messages
|
||||
ErrorHandler ErrorHandler
|
||||
|
||||
// Subscribers with the same queue name
|
||||
// will create a shared subscription where each
|
||||
// receives a subset of messages.
|
||||
@@ -59,9 +55,7 @@ func PublishContext(ctx context.Context) PublishOption {
|
||||
type SubscribeOption func(*SubscribeOptions)
|
||||
|
||||
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
|
||||
opt := SubscribeOptions{
|
||||
AutoAck: true,
|
||||
}
|
||||
opt := SubscribeOptions{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
@@ -85,18 +79,10 @@ func Codec(c codec.Marshaler) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// DisableAutoAck will disable auto acking of messages
|
||||
// after they have been handled.
|
||||
func DisableAutoAck() SubscribeOption {
|
||||
return func(o *SubscribeOptions) {
|
||||
o.AutoAck = false
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorHandler will catch all broker errors that cant be handled
|
||||
// in normal way, for example Codec errors
|
||||
func ErrorHandler(h Handler) Option {
|
||||
return func(o *Options) {
|
||||
func HandleError(h ErrorHandler) SubscribeOption {
|
||||
return func(o *SubscribeOptions) {
|
||||
o.ErrorHandler = h
|
||||
}
|
||||
}
|
||||
|
32
build/build.go
Normal file
32
build/build.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Package build is for building source into a package
|
||||
package build
|
||||
|
||||
// Build is an interface for building packages
|
||||
type Build interface {
|
||||
// Package builds a package
|
||||
Package(name string, src *Source) (*Package, error)
|
||||
// Remove removes the package
|
||||
Remove(*Package) error
|
||||
}
|
||||
|
||||
// Source is the source of a build
|
||||
type Source struct {
|
||||
// Path to the source if local
|
||||
Path string
|
||||
// Language is the language of code
|
||||
Language string
|
||||
// Location of the source
|
||||
Repository string
|
||||
}
|
||||
|
||||
// Package is packaged format for source
|
||||
type Package struct {
|
||||
// Name of the package
|
||||
Name string
|
||||
// Location of the package
|
||||
Path string
|
||||
// Type of package e.g tarball, binary, docker
|
||||
Type string
|
||||
// Source of the package
|
||||
Source *Source
|
||||
}
|
@@ -9,17 +9,17 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
"github.com/micro/go-micro/v3/build"
|
||||
"github.com/micro/go-micro/v3/logger"
|
||||
"github.com/micro/go-micro/v3/runtime/local/build"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
type dockerBuild struct {
|
||||
Options build.Options
|
||||
Client *docker.Client
|
||||
}
|
||||
|
||||
func (d *Builder) Build(s *build.Source) (*build.Package, error) {
|
||||
image := filepath.Join(s.Repository.Path, s.Repository.Name)
|
||||
func (d *dockerBuild) Package(name string, s *build.Source) (*build.Package, error) {
|
||||
image := name
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
@@ -28,7 +28,7 @@ func (d *Builder) Build(s *build.Source) (*build.Package, error) {
|
||||
dockerFile := "Dockerfile"
|
||||
|
||||
// open docker file
|
||||
f, err := os.Open(filepath.Join(s.Repository.Path, s.Repository.Name, dockerFile))
|
||||
f, err := os.Open(filepath.Join(s.Path, dockerFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,12 +71,15 @@ func (d *Builder) Build(s *build.Source) (*build.Package, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Builder) Clean(b *build.Package) error {
|
||||
image := filepath.Join(b.Path, b.Name)
|
||||
return d.Client.RemoveImage(image)
|
||||
func (d *dockerBuild) Remove(b *build.Package) error {
|
||||
return d.Client.RemoveImage(b.Name)
|
||||
}
|
||||
|
||||
func NewBuilder(opts ...build.Option) build.Builder {
|
||||
func (d *dockerBuild) String() string {
|
||||
return "docker"
|
||||
}
|
||||
|
||||
func NewBuild(opts ...build.Option) build.Build {
|
||||
options := build.Options{}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
@@ -86,7 +89,7 @@ func NewBuilder(opts ...build.Option) build.Builder {
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
return &Builder{
|
||||
return &dockerBuild{
|
||||
Options: options,
|
||||
Client: client,
|
||||
}
|
@@ -6,10 +6,10 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/micro/go-micro/v3/runtime/local/build"
|
||||
"github.com/micro/go-micro/v3/build"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
type goBuild struct {
|
||||
Options build.Options
|
||||
Cmd string
|
||||
Path string
|
||||
@@ -34,35 +34,40 @@ func whichGo() string {
|
||||
return "go"
|
||||
}
|
||||
|
||||
func (g *Builder) Build(s *build.Source) (*build.Package, error) {
|
||||
binary := filepath.Join(g.Path, s.Repository.Name)
|
||||
source := filepath.Join(s.Repository.Path, s.Repository.Name)
|
||||
func (g *goBuild) Package(name string, src *build.Source) (*build.Package, error) {
|
||||
binary := filepath.Join(g.Path, name)
|
||||
source := src.Path
|
||||
|
||||
cmd := exec.Command(g.Cmd, "build", "-o", binary, source)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &build.Package{
|
||||
Name: s.Repository.Name,
|
||||
Name: name,
|
||||
Path: binary,
|
||||
Type: "go",
|
||||
Source: s,
|
||||
Type: g.String(),
|
||||
Source: src,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *Builder) Clean(b *build.Package) error {
|
||||
func (g *goBuild) Remove(b *build.Package) error {
|
||||
binary := filepath.Join(b.Path, b.Name)
|
||||
return os.Remove(binary)
|
||||
}
|
||||
|
||||
func NewBuild(opts ...build.Option) build.Builder {
|
||||
func (g *goBuild) String() string {
|
||||
return "golang"
|
||||
}
|
||||
|
||||
func NewBuild(opts ...build.Option) build.Build {
|
||||
options := build.Options{
|
||||
Path: os.TempDir(),
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
return &Builder{
|
||||
return &goBuild{
|
||||
Options: options,
|
||||
Cmd: whichGo(),
|
||||
Path: options.Path,
|
45
build/tar/tar.go
Normal file
45
build/tar/tar.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Package tar basically tarballs source code
|
||||
package tar
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/micro/go-micro/v3/build"
|
||||
)
|
||||
|
||||
type tarBuild struct{}
|
||||
|
||||
func (t *tarBuild) Package(name string, src *build.Source) (*build.Package, error) {
|
||||
pkg := name + ".tar.gz"
|
||||
// path to the tarball
|
||||
path := filepath.Join(os.TempDir(), src.Path, pkg)
|
||||
|
||||
// create a temp directory
|
||||
if err := os.MkdirAll(filepath.Join(os.TempDir(), src.Path), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := Compress(src.Path, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &build.Package{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Type: t.String(),
|
||||
Source: src,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *tarBuild) Remove(b *build.Package) error {
|
||||
return os.Remove(b.Path)
|
||||
}
|
||||
|
||||
func (t *tarBuild) String() string {
|
||||
return "tar.gz"
|
||||
}
|
||||
|
||||
func NewBuild(opts ...build.Option) build.Build {
|
||||
return new(tarBuild)
|
||||
}
|
92
build/tar/util.go
Normal file
92
build/tar/util.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package tar
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Compress(source, dest string) error {
|
||||
// tar + gzip
|
||||
var buf bytes.Buffer
|
||||
_ = compress(source, &buf)
|
||||
|
||||
// write the .tar.gzip
|
||||
fileToWrite, err := os.OpenFile(dest, os.O_CREATE|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(fileToWrite, &buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func compress(src string, buf io.Writer) error {
|
||||
// tar > gzip > buf
|
||||
zr := gzip.NewWriter(buf)
|
||||
tw := tar.NewWriter(zr)
|
||||
|
||||
// walk through every file in the folder
|
||||
filepath.Walk(src, func(file string, fi os.FileInfo, err error) error {
|
||||
|
||||
// generate tar header
|
||||
header, err := tar.FileInfoHeader(fi, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// must provide real name
|
||||
// (see https://golang.org/src/archive/tar/common.go?#L626)
|
||||
|
||||
srcWithSlash := src
|
||||
if !strings.HasSuffix(src, string(filepath.Separator)) {
|
||||
srcWithSlash = src + string(filepath.Separator)
|
||||
}
|
||||
header.Name = strings.ReplaceAll(file, srcWithSlash, "")
|
||||
if header.Name == src || len(strings.TrimSpace(header.Name)) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// @todo This is a quick hack to speed up whole repo uploads
|
||||
// https://github.com/micro/micro/pull/956
|
||||
if !fi.IsDir() && !strings.HasSuffix(header.Name, ".go") &&
|
||||
!strings.HasSuffix(header.Name, ".mod") &&
|
||||
!strings.HasSuffix(header.Name, ".sum") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// write header
|
||||
if err := tw.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if not a dir, write file content
|
||||
|
||||
data, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(tw, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// produce tar
|
||||
if err := tw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// produce gzip
|
||||
if err := zr.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
//
|
||||
return nil
|
||||
}
|
80
cache/memcache/memcache.go
vendored
80
cache/memcache/memcache.go
vendored
@@ -1,80 +0,0 @@
|
||||
// Package memcache is a memcache implementation of the Cache
|
||||
package memcache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
"github.com/micro/go-micro/v3/cache"
|
||||
)
|
||||
|
||||
type memcacheCache struct {
|
||||
options cache.Options
|
||||
client *memcache.Client
|
||||
}
|
||||
|
||||
type memcacheItem struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (m *memcacheCache) Init(opts ...cache.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&m.options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memcacheCache) Get(key string) (interface{}, error) {
|
||||
item, err := m.client.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var mc *memcacheItem
|
||||
|
||||
if err := json.Unmarshal(item.Value, &mc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mc.Value, nil
|
||||
}
|
||||
|
||||
func (m *memcacheCache) Set(key string, val interface{}) error {
|
||||
b, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.client.Set(&memcache.Item{
|
||||
Key: key,
|
||||
Value: b,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *memcacheCache) Delete(key string) error {
|
||||
return m.client.Delete(key)
|
||||
}
|
||||
|
||||
func (m *memcacheCache) String() string {
|
||||
return "memcache"
|
||||
}
|
||||
|
||||
// NewCache returns a new memcache Cache
|
||||
func NewCache(opts ...cache.Option) cache.Cache {
|
||||
var options cache.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
// get and set the nodes
|
||||
nodes := options.Nodes
|
||||
if len(nodes) == 0 {
|
||||
nodes = []string{"localhost:11211"}
|
||||
}
|
||||
|
||||
return &memcacheCache{
|
||||
options: options,
|
||||
client: memcache.New(nodes...),
|
||||
}
|
||||
}
|
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/codec"
|
||||
)
|
||||
|
||||
func TestBackoff(t *testing.T) {
|
||||
@@ -32,3 +34,63 @@ func TestBackoff(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testRequest struct {
|
||||
service string
|
||||
method string
|
||||
endpoint string
|
||||
contentType string
|
||||
codec codec.Codec
|
||||
body interface{}
|
||||
opts RequestOptions
|
||||
}
|
||||
|
||||
func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
|
||||
var opts RequestOptions
|
||||
|
||||
for _, o := range reqOpts {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
// set the content-type specified
|
||||
if len(opts.ContentType) > 0 {
|
||||
contentType = opts.ContentType
|
||||
}
|
||||
|
||||
return &testRequest{
|
||||
service: service,
|
||||
method: endpoint,
|
||||
endpoint: endpoint,
|
||||
body: request,
|
||||
contentType: contentType,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *testRequest) ContentType() string {
|
||||
return r.contentType
|
||||
}
|
||||
|
||||
func (r *testRequest) Service() string {
|
||||
return r.service
|
||||
}
|
||||
|
||||
func (r *testRequest) Method() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
func (r *testRequest) Endpoint() string {
|
||||
return r.endpoint
|
||||
}
|
||||
|
||||
func (r *testRequest) Body() interface{} {
|
||||
return r.body
|
||||
}
|
||||
|
||||
func (r *testRequest) Codec() codec.Writer {
|
||||
return r.codec
|
||||
}
|
||||
|
||||
func (r *testRequest) Stream() bool {
|
||||
return r.opts.Stream
|
||||
}
|
||||
|
@@ -1,66 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
// NewCache returns an initialised cache.
|
||||
func NewCache() *Cache {
|
||||
return &Cache{
|
||||
cache: cache.New(cache.NoExpiration, 30*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// Cache for responses
|
||||
type Cache struct {
|
||||
cache *cache.Cache
|
||||
}
|
||||
|
||||
// Get a response from the cache
|
||||
func (c *Cache) Get(ctx context.Context, req Request) (interface{}, bool) {
|
||||
return c.cache.Get(key(ctx, req))
|
||||
}
|
||||
|
||||
// Set a response in the cache
|
||||
func (c *Cache) Set(ctx context.Context, req Request, rsp interface{}, expiry time.Duration) {
|
||||
c.cache.Set(key(ctx, req), rsp, expiry)
|
||||
}
|
||||
|
||||
// List the key value pairs in the cache
|
||||
func (c *Cache) List() map[string]string {
|
||||
items := c.cache.Items()
|
||||
|
||||
rsp := make(map[string]string, len(items))
|
||||
for k, v := range items {
|
||||
bytes, _ := json.Marshal(v.Object)
|
||||
rsp[k] = string(bytes)
|
||||
}
|
||||
|
||||
return rsp
|
||||
}
|
||||
|
||||
// key returns a hash for the context and request
|
||||
func key(ctx context.Context, req Request) string {
|
||||
ns, _ := metadata.Get(ctx, "Micro-Namespace")
|
||||
|
||||
bytes, _ := json.Marshal(map[string]interface{}{
|
||||
"namespace": ns,
|
||||
"request": map[string]interface{}{
|
||||
"service": req.Service(),
|
||||
"endpoint": req.Endpoint(),
|
||||
"method": req.Method(),
|
||||
"body": req.Body(),
|
||||
},
|
||||
})
|
||||
|
||||
h := fnv.New64()
|
||||
h.Write(bytes)
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
@@ -1,77 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
req := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"}
|
||||
|
||||
t.Run("CacheMiss", func(t *testing.T) {
|
||||
if _, ok := NewCache().Get(ctx, req); ok {
|
||||
t.Errorf("Expected to get no result from Get")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CacheHit", func(t *testing.T) {
|
||||
c := NewCache()
|
||||
|
||||
rsp := "theresponse"
|
||||
c.Set(ctx, req, rsp, time.Minute)
|
||||
|
||||
if res, ok := c.Get(ctx, req); !ok {
|
||||
t.Errorf("Expected a result, got nothing")
|
||||
} else if res != rsp {
|
||||
t.Errorf("Expected '%v' result, got '%v'", rsp, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCacheKey(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
req1 := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar"}
|
||||
req2 := &testRequest{service: "go.micro.service.foo", method: "Foo.Baz"}
|
||||
req3 := &testRequest{service: "go.micro.service.foo", method: "Foo.Bar", body: "customquery"}
|
||||
|
||||
t.Run("IdenticalRequests", func(t *testing.T) {
|
||||
key1 := key(ctx, req1)
|
||||
key2 := key(ctx, req1)
|
||||
if key1 != key2 {
|
||||
t.Errorf("Expected the keys to match for identical requests and context")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DifferentRequestEndpoints", func(t *testing.T) {
|
||||
key1 := key(ctx, req1)
|
||||
key2 := key(ctx, req2)
|
||||
|
||||
if key1 == key2 {
|
||||
t.Errorf("Expected the keys to differ for different request endpoints")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DifferentRequestBody", func(t *testing.T) {
|
||||
key1 := key(ctx, req2)
|
||||
key2 := key(ctx, req3)
|
||||
|
||||
if key1 == key2 {
|
||||
t.Errorf("Expected the keys to differ for different request bodies")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DifferentMetadata", func(t *testing.T) {
|
||||
mdCtx := metadata.Set(context.TODO(), "Micro-Namespace", "bar")
|
||||
key1 := key(mdCtx, req1)
|
||||
key2 := key(ctx, req1)
|
||||
|
||||
if key1 == key2 {
|
||||
t.Errorf("Expected the keys to differ for different metadata")
|
||||
}
|
||||
})
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type clientKey struct{}
|
||||
|
||||
func FromContext(ctx context.Context) (Client, bool) {
|
||||
c, ok := ctx.Value(clientKey{}).(Client)
|
||||
return c, ok
|
||||
}
|
||||
|
||||
func NewContext(ctx context.Context, c Client) context.Context {
|
||||
return context.WithValue(ctx, clientKey{}, c)
|
||||
}
|
@@ -16,7 +16,6 @@ import (
|
||||
raw "github.com/micro/go-micro/v3/codec/bytes"
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@@ -70,7 +69,7 @@ func (g *grpcClient) secure(addr string) grpc.DialOption {
|
||||
return grpc.WithInsecure()
|
||||
}
|
||||
|
||||
func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
func (g *grpcClient) call(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
var header map[string]string
|
||||
|
||||
header = make(map[string]string)
|
||||
@@ -103,7 +102,7 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
|
||||
grpcDialOptions := []grpc.DialOption{
|
||||
grpc.WithTimeout(opts.DialTimeout),
|
||||
g.secure(node.Address),
|
||||
g.secure(addr),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
|
||||
grpc.MaxCallSendMsgSize(maxSendMsgSize),
|
||||
@@ -114,13 +113,13 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
|
||||
cc, err := g.pool.getConn(node.Address, grpcDialOptions...)
|
||||
cc, err := g.pool.getConn(addr, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
defer func() {
|
||||
// defer execution of release
|
||||
g.pool.release(node.Address, cc, grr)
|
||||
g.pool.release(addr, cc, grr)
|
||||
}()
|
||||
|
||||
ch := make(chan error, 1)
|
||||
@@ -146,7 +145,7 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R
|
||||
return grr
|
||||
}
|
||||
|
||||
func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
func (g *grpcClient) stream(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
var header map[string]string
|
||||
|
||||
if md, ok := metadata.FromContext(ctx); ok {
|
||||
@@ -173,27 +172,18 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
var dialCtx context.Context
|
||||
var cancel context.CancelFunc
|
||||
if opts.DialTimeout >= 0 {
|
||||
dialCtx, cancel = context.WithTimeout(ctx, opts.DialTimeout)
|
||||
} else {
|
||||
dialCtx, cancel = context.WithCancel(ctx)
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
wc := wrapCodec{cf}
|
||||
|
||||
grpcDialOptions := []grpc.DialOption{
|
||||
grpc.WithTimeout(opts.DialTimeout),
|
||||
g.secure(node.Address),
|
||||
g.secure(addr),
|
||||
}
|
||||
|
||||
if opts := g.getGrpcDialOptions(); opts != nil {
|
||||
grpcDialOptions = append(grpcDialOptions, opts...)
|
||||
}
|
||||
|
||||
cc, err := grpc.DialContext(dialCtx, node.Address, grpcDialOptions...)
|
||||
cc, err := g.pool.getConn(addr, grpcDialOptions...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
@@ -212,16 +202,16 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
grpcCallOptions = append(grpcCallOptions, opts...)
|
||||
}
|
||||
|
||||
// create a new cancelling context
|
||||
newCtx, cancel := context.WithCancel(ctx)
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
|
||||
st, err := cc.NewStream(newCtx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...)
|
||||
st, err := cc.NewStream(ctx, desc, methodToGRPC(req.Service(), req.Endpoint()), grpcCallOptions...)
|
||||
if err != nil {
|
||||
// we need to cleanup as we dialled and created a context
|
||||
// cancel the context
|
||||
cancel()
|
||||
// close the connection
|
||||
cc.Close()
|
||||
// release the connection
|
||||
g.pool.release(addr, cc, err)
|
||||
// now return the error
|
||||
return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err))
|
||||
}
|
||||
@@ -238,17 +228,25 @@ func (g *grpcClient) stream(ctx context.Context, node *registry.Node, req client
|
||||
|
||||
// setup the stream response
|
||||
stream := &grpcStream{
|
||||
context: ctx,
|
||||
request: req,
|
||||
ClientStream: st,
|
||||
context: ctx,
|
||||
request: req,
|
||||
response: &response{
|
||||
conn: cc,
|
||||
stream: st,
|
||||
codec: cf,
|
||||
gcodec: codec,
|
||||
},
|
||||
stream: st,
|
||||
conn: cc,
|
||||
cancel: cancel,
|
||||
conn: cc,
|
||||
close: func(err error) {
|
||||
// cancel the context if an error occured
|
||||
if err != nil {
|
||||
cancel()
|
||||
}
|
||||
|
||||
// defer execution of release
|
||||
g.pool.release(addr, cc, err)
|
||||
},
|
||||
}
|
||||
|
||||
// set the stream as the response
|
||||
@@ -389,6 +387,34 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
|
||||
gcall = callOpts.CallWrappers[i-1](gcall)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = g.opts.Router
|
||||
}
|
||||
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = g.opts.Selector
|
||||
}
|
||||
|
||||
// inject proxy address
|
||||
// TODO: don't even bother using Lookup/Select in this case
|
||||
if len(g.opts.Proxy) > 0 {
|
||||
callOpts.Address = []string{g.opts.Proxy}
|
||||
}
|
||||
|
||||
// lookup the route to send the reques to
|
||||
// TODO apply any filtering here
|
||||
routes, err := g.opts.Lookup(ctx, req, callOpts)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// balance the list of nodes
|
||||
next, err := callOpts.Selector.Select(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// return errors.New("go.micro.client", "request timeout", 408)
|
||||
call := func(i int) error {
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
@@ -402,31 +428,14 @@ func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface
|
||||
time.Sleep(t)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = g.opts.Router
|
||||
}
|
||||
// use the selector passed as a call option, or fallback to the rpc clients selector
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = g.opts.Selector
|
||||
}
|
||||
|
||||
// lookup the route to send the reques to
|
||||
route, err := client.LookupRoute(req, callOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// pass a node to enable backwards compatability as changing the
|
||||
// call func would be a breaking change.
|
||||
// todo v3: change the call func to accept a route
|
||||
node := ®istry.Node{Address: route.Address}
|
||||
// get the next node
|
||||
node := next()
|
||||
|
||||
// make the call
|
||||
err = gcall(ctx, node, req, rsp, callOpts)
|
||||
|
||||
// record the result of the call to inform future routing decisions
|
||||
g.opts.Selector.Record(*route, err)
|
||||
g.opts.Selector.Record(node, err)
|
||||
|
||||
// try and transform the error to a go-micro error
|
||||
if verr, ok := err.(*errors.Error); ok {
|
||||
@@ -493,6 +502,34 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
||||
gstream = callOpts.CallWrappers[i-1](gstream)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = g.opts.Router
|
||||
}
|
||||
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = g.opts.Selector
|
||||
}
|
||||
|
||||
// inject proxy address
|
||||
// TODO: don't even bother using Lookup/Select in this case
|
||||
if len(g.opts.Proxy) > 0 {
|
||||
callOpts.Address = []string{g.opts.Proxy}
|
||||
}
|
||||
|
||||
// lookup the route to send the reques to
|
||||
// TODO: move to internal lookup func
|
||||
routes, err := g.opts.Lookup(ctx, req, callOpts)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// balance the list of nodes
|
||||
next, err := callOpts.Selector.Select(routes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
call := func(i int) (client.Stream, error) {
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
t, err := callOpts.Backoff(ctx, req, i)
|
||||
@@ -505,39 +542,22 @@ func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
||||
time.Sleep(t)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = g.opts.Router
|
||||
}
|
||||
// use the selector passed as a call option, or fallback to the rpc clients selector
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = g.opts.Selector
|
||||
}
|
||||
|
||||
// lookup the route to send the reques to
|
||||
route, err := client.LookupRoute(req, callOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// pass a node to enable backwards compatability as changing the
|
||||
// call func would be a breaking change.
|
||||
// todo v3: change the call func to accept a route
|
||||
node := ®istry.Node{Address: route.Address}
|
||||
// get the next node
|
||||
node := next()
|
||||
|
||||
// make the call
|
||||
stream := &grpcStream{}
|
||||
err = g.stream(ctx, node, req, stream, callOpts)
|
||||
|
||||
// record the result of the call to inform future routing decisions
|
||||
g.opts.Selector.Record(*route, err)
|
||||
g.opts.Selector.Record(node, err)
|
||||
|
||||
// try and transform the error to a go-micro error
|
||||
if verr, ok := err.(*errors.Error); ok {
|
||||
return nil, verr
|
||||
}
|
||||
|
||||
g.opts.Selector.Record(*route, err)
|
||||
g.opts.Selector.Record(node, err)
|
||||
return stream, err
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type response struct {
|
||||
conn *grpc.ClientConn
|
||||
conn *poolConn
|
||||
stream grpc.ClientStream
|
||||
codec encoding.Codec
|
||||
gcodec codec.Codec
|
||||
|
@@ -11,15 +11,17 @@ import (
|
||||
|
||||
// Implements the streamer interface
|
||||
type grpcStream struct {
|
||||
// embed so we can access if need be
|
||||
grpc.ClientStream
|
||||
|
||||
sync.RWMutex
|
||||
closed bool
|
||||
err error
|
||||
conn *grpc.ClientConn
|
||||
stream grpc.ClientStream
|
||||
conn *poolConn
|
||||
request client.Request
|
||||
response client.Response
|
||||
context context.Context
|
||||
cancel func()
|
||||
close func(err error)
|
||||
}
|
||||
|
||||
func (g *grpcStream) Context() context.Context {
|
||||
@@ -35,7 +37,7 @@ func (g *grpcStream) Response() client.Response {
|
||||
}
|
||||
|
||||
func (g *grpcStream) Send(msg interface{}) error {
|
||||
if err := g.stream.SendMsg(msg); err != nil {
|
||||
if err := g.ClientStream.SendMsg(msg); err != nil {
|
||||
g.setError(err)
|
||||
return err
|
||||
}
|
||||
@@ -44,7 +46,8 @@ func (g *grpcStream) Send(msg interface{}) error {
|
||||
|
||||
func (g *grpcStream) Recv(msg interface{}) (err error) {
|
||||
defer g.setError(err)
|
||||
if err = g.stream.RecvMsg(msg); err != nil {
|
||||
|
||||
if err = g.ClientStream.RecvMsg(msg); err != nil {
|
||||
// #202 - inconsistent gRPC stream behavior
|
||||
// the only way to tell if the stream is done is when we get a EOF on the Recv
|
||||
// here we should close the underlying gRPC ClientConn
|
||||
@@ -52,7 +55,10 @@ func (g *grpcStream) Recv(msg interface{}) (err error) {
|
||||
if err == io.EOF && closeErr != nil {
|
||||
err = closeErr
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,9 +86,9 @@ func (g *grpcStream) Close() error {
|
||||
if g.closed {
|
||||
return nil
|
||||
}
|
||||
// cancel the context
|
||||
g.cancel()
|
||||
|
||||
// close the connection
|
||||
g.closed = true
|
||||
g.stream.CloseSend()
|
||||
return g.conn.Close()
|
||||
g.close(g.err)
|
||||
return g.ClientStream.CloseSend()
|
||||
}
|
||||
|
50
client/lookup.go
Normal file
50
client/lookup.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/router"
|
||||
)
|
||||
|
||||
// LookupFunc is used to lookup routes for a service
|
||||
type LookupFunc func(context.Context, Request, CallOptions) ([]string, error)
|
||||
|
||||
// LookupRoute for a request using the router and then choose one using the selector
|
||||
func LookupRoute(ctx context.Context, req Request, opts CallOptions) ([]string, error) {
|
||||
// check to see if an address was provided as a call option
|
||||
if len(opts.Address) > 0 {
|
||||
return opts.Address, nil
|
||||
}
|
||||
|
||||
// construct the router query
|
||||
query := []router.LookupOption{}
|
||||
|
||||
// if a custom network was requested, pass this to the router. By default the router will use it's
|
||||
// own network, which is set during initialisation.
|
||||
if len(opts.Network) > 0 {
|
||||
query = append(query, router.LookupNetwork(opts.Network))
|
||||
}
|
||||
|
||||
// lookup the routes which can be used to execute the request
|
||||
routes, err := opts.Router.Lookup(req.Service(), query...)
|
||||
if err == router.ErrRouteNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", req.Service(), err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", req.Service(), err.Error())
|
||||
}
|
||||
|
||||
// sort by lowest metric first
|
||||
sort.Slice(routes, func(i, j int) bool {
|
||||
return routes[i].Metric < routes[j].Metric
|
||||
})
|
||||
|
||||
var addrs []string
|
||||
|
||||
for _, route := range routes {
|
||||
addrs = append(addrs, route.Address)
|
||||
}
|
||||
|
||||
return addrs, nil
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
// Package mucp provides an mucp client
|
||||
// Package mucp provides a transport agnostic RPC client
|
||||
package mucp
|
||||
|
||||
import (
|
||||
@@ -14,18 +14,11 @@ import (
|
||||
raw "github.com/micro/go-micro/v3/codec/bytes"
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/metadata"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
"github.com/micro/go-micro/v3/transport"
|
||||
"github.com/micro/go-micro/v3/network/transport"
|
||||
"github.com/micro/go-micro/v3/util/buf"
|
||||
"github.com/micro/go-micro/v3/util/net"
|
||||
"github.com/micro/go-micro/v3/util/pool"
|
||||
)
|
||||
|
||||
// NewClient returns a new micro client interface
|
||||
func NewClient(opts ...client.Option) client.Client {
|
||||
return newClient(opts...)
|
||||
}
|
||||
|
||||
type rpcClient struct {
|
||||
once atomic.Value
|
||||
opts client.Options
|
||||
@@ -33,7 +26,8 @@ type rpcClient struct {
|
||||
seq uint64
|
||||
}
|
||||
|
||||
func newClient(opt ...client.Option) client.Client {
|
||||
// NewClient returns a new micro client interface
|
||||
func NewClient(opt ...client.Option) client.Client {
|
||||
opts := client.NewOptions(opt...)
|
||||
|
||||
p := pool.NewPool(
|
||||
@@ -69,7 +63,7 @@ func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) {
|
||||
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
|
||||
}
|
||||
|
||||
func (r *rpcClient) call(ctx context.Context, node *registry.Node, req client.Request, resp interface{}, opts client.CallOptions) error {
|
||||
func (r *rpcClient) call(ctx context.Context, addr string, req client.Request, resp interface{}, opts client.CallOptions) error {
|
||||
msg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
@@ -93,16 +87,9 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req client.Re
|
||||
// set the accept header
|
||||
msg.Header["Accept"] = req.ContentType()
|
||||
|
||||
// setup old protocol
|
||||
cf := setupProtocol(msg, node)
|
||||
|
||||
// no codec specified
|
||||
if cf == nil {
|
||||
var err error
|
||||
cf, err = r.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
cf, err := r.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
dOpts := []transport.DialOption{
|
||||
@@ -113,7 +100,7 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req client.Re
|
||||
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
|
||||
}
|
||||
|
||||
c, err := r.pool.Get(node.Address, dOpts...)
|
||||
c, err := r.pool.Get(addr, dOpts...)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
@@ -186,7 +173,7 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req client.Re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req client.Request, opts client.CallOptions) (client.Stream, error) {
|
||||
func (r *rpcClient) stream(ctx context.Context, addr string, req client.Request, opts client.CallOptions) (client.Stream, error) {
|
||||
msg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
@@ -207,16 +194,9 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req client.
|
||||
// set the accept header
|
||||
msg.Header["Accept"] = req.ContentType()
|
||||
|
||||
// set old codecs
|
||||
cf := setupProtocol(msg, node)
|
||||
|
||||
// no codec specified
|
||||
if cf == nil {
|
||||
var err error
|
||||
cf, err = r.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
cf, err := r.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
dOpts := []transport.DialOption{
|
||||
@@ -227,7 +207,7 @@ func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req client.
|
||||
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
|
||||
}
|
||||
|
||||
c, err := r.opts.Transport.Dial(node.Address, dOpts...)
|
||||
c, err := r.opts.Transport.Dial(addr, dOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
|
||||
}
|
||||
@@ -357,6 +337,34 @@ func (r *rpcClient) Call(ctx context.Context, request client.Request, response i
|
||||
rcall = callOpts.CallWrappers[i-1](rcall)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = r.opts.Router
|
||||
}
|
||||
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = r.opts.Selector
|
||||
}
|
||||
|
||||
// inject proxy address
|
||||
// TODO: don't even bother using Lookup/Select in this case
|
||||
if len(r.opts.Proxy) > 0 {
|
||||
callOpts.Address = []string{r.opts.Proxy}
|
||||
}
|
||||
|
||||
// lookup the route to send the reques to
|
||||
// TODO apply any filtering here
|
||||
routes, err := r.opts.Lookup(ctx, request, callOpts)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// balance the list of nodes
|
||||
next, err := callOpts.Selector.Select(routes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// return errors.New("go.micro.client", "request timeout", 408)
|
||||
call := func(i int) error {
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
@@ -370,31 +378,14 @@ func (r *rpcClient) Call(ctx context.Context, request client.Request, response i
|
||||
time.Sleep(t)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = r.opts.Router
|
||||
}
|
||||
// use the selector passed as a call option, or fallback to the rpc clients selector
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = r.opts.Selector
|
||||
}
|
||||
|
||||
// lookup the route to send the request via
|
||||
route, err := client.LookupRoute(request, callOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// pass a node to enable backwards comparability as changing the
|
||||
// call func would be a breaking change.
|
||||
// todo v3: change the call func to accept a route
|
||||
node := ®istry.Node{Address: route.Address, Metadata: route.Metadata}
|
||||
// get the next node
|
||||
node := next()
|
||||
|
||||
// make the call
|
||||
err = rcall(ctx, node, request, response, callOpts)
|
||||
|
||||
// record the result of the call to inform future routing decisions
|
||||
r.opts.Selector.Record(*route, err)
|
||||
r.opts.Selector.Record(node, err)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -403,7 +394,7 @@ func (r *rpcClient) Call(ctx context.Context, request client.Request, response i
|
||||
retries := callOpts.Retries
|
||||
|
||||
// disable retries when using a proxy
|
||||
if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
|
||||
if len(r.opts.Proxy) > 0 {
|
||||
retries = 0
|
||||
}
|
||||
|
||||
@@ -454,6 +445,34 @@ func (r *rpcClient) Stream(ctx context.Context, request client.Request, opts ...
|
||||
default:
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = r.opts.Router
|
||||
}
|
||||
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = r.opts.Selector
|
||||
}
|
||||
|
||||
// inject proxy address
|
||||
// TODO: don't even bother using Lookup/Select in this case
|
||||
if len(r.opts.Proxy) > 0 {
|
||||
callOpts.Address = []string{r.opts.Proxy}
|
||||
}
|
||||
|
||||
// lookup the route to send the reques to
|
||||
// TODO apply any filtering here
|
||||
routes, err := r.opts.Lookup(ctx, request, callOpts)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
|
||||
// balance the list of nodes
|
||||
next, err := callOpts.Selector.Select(routes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
call := func(i int) (client.Stream, error) {
|
||||
// call backoff first. Someone may want an initial start delay
|
||||
t, err := callOpts.Backoff(ctx, request, i)
|
||||
@@ -466,31 +485,14 @@ func (r *rpcClient) Stream(ctx context.Context, request client.Request, opts ...
|
||||
time.Sleep(t)
|
||||
}
|
||||
|
||||
// use the router passed as a call option, or fallback to the rpc clients router
|
||||
if callOpts.Router == nil {
|
||||
callOpts.Router = r.opts.Router
|
||||
}
|
||||
// use the selector passed as a call option, or fallback to the rpc clients selector
|
||||
if callOpts.Selector == nil {
|
||||
callOpts.Selector = r.opts.Selector
|
||||
}
|
||||
|
||||
// lookup the route to send the request via
|
||||
route, err := client.LookupRoute(request, callOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// pass a node to enable backwards compatability as changing the
|
||||
// call func would be a breaking change.
|
||||
// todo v3: change the call func to accept a route
|
||||
node := ®istry.Node{Address: route.Address, Metadata: route.Metadata}
|
||||
// get the next node
|
||||
node := next()
|
||||
|
||||
// perform the call
|
||||
stream, err := r.stream(ctx, node, request, callOpts)
|
||||
|
||||
// record the result of the call to inform future routing decisions
|
||||
r.opts.Selector.Record(*route, err)
|
||||
r.opts.Selector.Record(node, err)
|
||||
|
||||
return stream, err
|
||||
}
|
||||
@@ -504,7 +506,7 @@ func (r *rpcClient) Stream(ctx context.Context, request client.Request, opts ...
|
||||
retries := callOpts.Retries
|
||||
|
||||
// disable retries when using a proxy
|
||||
if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
|
||||
if len(r.opts.Proxy) > 0 {
|
||||
retries = 0
|
||||
}
|
||||
|
||||
|
@@ -12,8 +12,8 @@ import (
|
||||
"github.com/micro/go-micro/v3/codec/proto"
|
||||
"github.com/micro/go-micro/v3/codec/protorpc"
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/network/transport"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
"github.com/micro/go-micro/v3/transport"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@@ -2,7 +2,7 @@ package mucp
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/v3/codec"
|
||||
"github.com/micro/go-micro/v3/transport"
|
||||
"github.com/micro/go-micro/v3/network/transport"
|
||||
)
|
||||
|
||||
type rpcResponse struct {
|
||||
|
@@ -9,10 +9,13 @@ import (
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
"github.com/micro/go-micro/v3/registry/memory"
|
||||
"github.com/micro/go-micro/v3/router"
|
||||
regRouter "github.com/micro/go-micro/v3/router/registry"
|
||||
)
|
||||
|
||||
func newTestRegistry() registry.Registry {
|
||||
return memory.NewRegistry(memory.Services(testData))
|
||||
func newTestRouter() router.Router {
|
||||
reg := memory.NewRegistry(memory.Services(testData))
|
||||
return regRouter.NewRouter(router.Registry(reg))
|
||||
}
|
||||
|
||||
func TestCallAddress(t *testing.T) {
|
||||
@@ -22,7 +25,7 @@ func TestCallAddress(t *testing.T) {
|
||||
address := "10.1.10.1:8080"
|
||||
|
||||
wrap := func(cf client.CallFunc) client.CallFunc {
|
||||
return func(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
return func(ctx context.Context, node string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
called = true
|
||||
|
||||
if req.Service() != service {
|
||||
@@ -33,8 +36,8 @@ func TestCallAddress(t *testing.T) {
|
||||
return fmt.Errorf("expected service: %s got %s", endpoint, req.Endpoint())
|
||||
}
|
||||
|
||||
if node.Address != address {
|
||||
return fmt.Errorf("expected address: %s got %s", address, node.Address)
|
||||
if node != address {
|
||||
return fmt.Errorf("expected address: %s got %s", address, node)
|
||||
}
|
||||
|
||||
// don't do the call
|
||||
@@ -42,9 +45,10 @@ func TestCallAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
r := newTestRegistry()
|
||||
r := newTestRouter()
|
||||
|
||||
c := NewClient(
|
||||
client.Registry(r),
|
||||
client.Router(r),
|
||||
client.WrapCall(wrap),
|
||||
)
|
||||
|
||||
@@ -69,7 +73,7 @@ func TestCallRetry(t *testing.T) {
|
||||
var called int
|
||||
|
||||
wrap := func(cf client.CallFunc) client.CallFunc {
|
||||
return func(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
return func(ctx context.Context, node string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
called++
|
||||
if called == 1 {
|
||||
return errors.InternalServerError("test.error", "retry request")
|
||||
@@ -80,9 +84,9 @@ func TestCallRetry(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
r := newTestRegistry()
|
||||
r := newTestRouter()
|
||||
c := NewClient(
|
||||
client.Registry(r),
|
||||
client.Router(r),
|
||||
client.WrapCall(wrap),
|
||||
)
|
||||
|
||||
@@ -107,7 +111,7 @@ func TestCallWrapper(t *testing.T) {
|
||||
address := "10.1.10.1:8080"
|
||||
|
||||
wrap := func(cf client.CallFunc) client.CallFunc {
|
||||
return func(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
return func(ctx context.Context, node string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||
called = true
|
||||
|
||||
if req.Service() != service {
|
||||
@@ -118,8 +122,8 @@ func TestCallWrapper(t *testing.T) {
|
||||
return fmt.Errorf("expected service: %s got %s", endpoint, req.Endpoint())
|
||||
}
|
||||
|
||||
if node.Address != address {
|
||||
return fmt.Errorf("expected address: %s got %s", address, node.Address)
|
||||
if node != address {
|
||||
return fmt.Errorf("expected address: %s got %s", address, node)
|
||||
}
|
||||
|
||||
// don't do the call
|
||||
@@ -127,22 +131,19 @@ func TestCallWrapper(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
r := newTestRegistry()
|
||||
r := newTestRouter()
|
||||
c := NewClient(
|
||||
client.Registry(r),
|
||||
client.Router(r),
|
||||
client.WrapCall(wrap),
|
||||
)
|
||||
|
||||
r.Register(®istry.Service{
|
||||
r.Options().Registry.Register(®istry.Service{
|
||||
Name: service,
|
||||
Version: "latest",
|
||||
Nodes: []*registry.Node{
|
||||
{
|
||||
Id: id,
|
||||
Address: address,
|
||||
Metadata: map[string]string{
|
||||
"protocol": "mucp",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/client"
|
||||
"github.com/micro/go-micro/v3/transport"
|
||||
"github.com/micro/go-micro/v3/network/transport"
|
||||
)
|
||||
|
||||
func TestCallOptions(t *testing.T) {
|
||||
|
@@ -7,16 +7,20 @@ import (
|
||||
"github.com/micro/go-micro/v3/broker"
|
||||
"github.com/micro/go-micro/v3/broker/http"
|
||||
"github.com/micro/go-micro/v3/codec"
|
||||
"github.com/micro/go-micro/v3/network/transport"
|
||||
thttp "github.com/micro/go-micro/v3/network/transport/http"
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
"github.com/micro/go-micro/v3/router"
|
||||
regRouter "github.com/micro/go-micro/v3/router/registry"
|
||||
"github.com/micro/go-micro/v3/selector"
|
||||
"github.com/micro/go-micro/v3/transport"
|
||||
"github.com/micro/go-micro/v3/selector/roundrobin"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
// Used to select codec
|
||||
ContentType string
|
||||
// Proxy address to send requests via
|
||||
Proxy string
|
||||
|
||||
// Plugged interfaces
|
||||
Broker broker.Broker
|
||||
@@ -25,13 +29,13 @@ type Options struct {
|
||||
Selector selector.Selector
|
||||
Transport transport.Transport
|
||||
|
||||
// Lookup used for looking up routes
|
||||
Lookup LookupFunc
|
||||
|
||||
// Connection Pool
|
||||
PoolSize int
|
||||
PoolTTL time.Duration
|
||||
|
||||
// Response cache
|
||||
Cache *Cache
|
||||
|
||||
// Middleware for client
|
||||
Wrappers []Wrapper
|
||||
|
||||
@@ -48,8 +52,6 @@ type CallOptions struct {
|
||||
Address []string
|
||||
// Backoff func
|
||||
Backoff BackoffFunc
|
||||
// Duration to cache the response for
|
||||
CacheExpiry time.Duration
|
||||
// Transport Dial Timeout
|
||||
DialTimeout time.Duration
|
||||
// Number of Call attempts
|
||||
@@ -66,8 +68,8 @@ type CallOptions struct {
|
||||
SelectOptions []selector.SelectOption
|
||||
// Stream timeout for the stream
|
||||
StreamTimeout time.Duration
|
||||
// Use the services own auth token
|
||||
ServiceToken bool
|
||||
// Use the auth token as the authorization header
|
||||
AuthToken bool
|
||||
// Network to lookup the route within
|
||||
Network string
|
||||
|
||||
@@ -102,7 +104,6 @@ type RequestOptions struct {
|
||||
|
||||
func NewOptions(options ...Option) Options {
|
||||
opts := Options{
|
||||
Cache: NewCache(),
|
||||
Context: context.Background(),
|
||||
ContentType: "application/protobuf",
|
||||
Codecs: make(map[string]codec.NewCodec),
|
||||
@@ -113,12 +114,13 @@ func NewOptions(options ...Option) Options {
|
||||
RequestTimeout: DefaultRequestTimeout,
|
||||
DialTimeout: transport.DefaultDialTimeout,
|
||||
},
|
||||
Lookup: LookupRoute,
|
||||
PoolSize: DefaultPoolSize,
|
||||
PoolTTL: DefaultPoolTTL,
|
||||
Broker: http.NewBroker(),
|
||||
Router: regRouter.NewRouter(),
|
||||
Selector: selector.DefaultSelector,
|
||||
Transport: transport.DefaultTransport,
|
||||
Selector: roundrobin.NewSelector(),
|
||||
Transport: thttp.NewTransport(),
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
@@ -149,6 +151,13 @@ func ContentType(ct string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Proxy sets the proxy address
|
||||
func Proxy(addr string) Option {
|
||||
return func(o *Options) {
|
||||
o.Proxy = addr
|
||||
}
|
||||
}
|
||||
|
||||
// PoolSize sets the connection pool size
|
||||
func PoolSize(d int) Option {
|
||||
return func(o *Options) {
|
||||
@@ -170,6 +179,13 @@ func Transport(t transport.Transport) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Registry sets the routers registry
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Router.Init(router.Registry(r))
|
||||
}
|
||||
}
|
||||
|
||||
// Router is used to lookup routes for a service
|
||||
func Router(r router.Router) Option {
|
||||
return func(o *Options) {
|
||||
@@ -206,6 +222,13 @@ func Backoff(fn BackoffFunc) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup sets the lookup function to use for resolving service names
|
||||
func Lookup(l LookupFunc) Option {
|
||||
return func(o *Options) {
|
||||
o.Lookup = l
|
||||
}
|
||||
}
|
||||
|
||||
// Number of retries when making the request.
|
||||
// Should this be a Call Option?
|
||||
func Retries(i int) Option {
|
||||
@@ -221,13 +244,6 @@ func Retry(fn RetryFunc) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Registry sets the routers registry
|
||||
func Registry(r registry.Registry) Option {
|
||||
return func(o *Options) {
|
||||
o.Router.Init(router.Registry(r))
|
||||
}
|
||||
}
|
||||
|
||||
// The request timeout.
|
||||
// Should this be a Call Option?
|
||||
func RequestTimeout(d time.Duration) Option {
|
||||
@@ -327,19 +343,11 @@ func WithDialTimeout(d time.Duration) CallOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithServiceToken is a CallOption which overrides the
|
||||
// WithAuthToken is a CallOption which overrides the
|
||||
// authorization header with the services own auth token
|
||||
func WithServiceToken() CallOption {
|
||||
func WithAuthToken() CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.ServiceToken = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithCache is a CallOption which sets the duration the response
|
||||
// shoull be cached for
|
||||
func WithCache(c time.Duration) CallOption {
|
||||
return func(o *CallOptions) {
|
||||
o.CacheExpiry = c
|
||||
o.AuthToken = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,402 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: client/service/proto/client.proto
|
||||
|
||||
package go_micro_client
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Request struct {
|
||||
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
|
||||
Endpoint string `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
|
||||
ContentType string `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
Body []byte `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
func (*Request) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_27c3d425ddd1a066, []int{0}
|
||||
}
|
||||
|
||||
func (m *Request) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Request.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Request) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Request.Merge(m, src)
|
||||
}
|
||||
func (m *Request) XXX_Size() int {
|
||||
return xxx_messageInfo_Request.Size(m)
|
||||
}
|
||||
func (m *Request) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Request.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Request proto.InternalMessageInfo
|
||||
|
||||
func (m *Request) GetService() string {
|
||||
if m != nil {
|
||||
return m.Service
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetEndpoint() string {
|
||||
if m != nil {
|
||||
return m.Endpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetContentType() string {
|
||||
if m != nil {
|
||||
return m.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Request) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Body []byte `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return proto.CompactTextString(m) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
func (*Response) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_27c3d425ddd1a066, []int{1}
|
||||
}
|
||||
|
||||
func (m *Response) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Response.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Response) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Response.Merge(m, src)
|
||||
}
|
||||
func (m *Response) XXX_Size() int {
|
||||
return xxx_messageInfo_Response.Size(m)
|
||||
}
|
||||
func (m *Response) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Response.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Response proto.InternalMessageInfo
|
||||
|
||||
func (m *Response) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"`
|
||||
ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
|
||||
Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_27c3d425ddd1a066, []int{2}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
func (m *Message) GetTopic() string {
|
||||
if m != nil {
|
||||
return m.Topic
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetContentType() string {
|
||||
if m != nil {
|
||||
return m.ContentType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Message) GetBody() []byte {
|
||||
if m != nil {
|
||||
return m.Body
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Request)(nil), "go.micro.client.Request")
|
||||
proto.RegisterType((*Response)(nil), "go.micro.client.Response")
|
||||
proto.RegisterType((*Message)(nil), "go.micro.client.Message")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("client/service/proto/client.proto", fileDescriptor_27c3d425ddd1a066) }
|
||||
|
||||
var fileDescriptor_27c3d425ddd1a066 = []byte{
|
||||
// 267 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0xc1, 0x4b, 0xc3, 0x30,
|
||||
0x14, 0xc6, 0x97, 0x6d, 0xb6, 0xf3, 0x39, 0x10, 0x1e, 0x1e, 0x62, 0x0f, 0xb2, 0xf5, 0xd4, 0x53,
|
||||
0x2b, 0x7a, 0x16, 0x0f, 0x3d, 0x0b, 0x52, 0xc5, 0xab, 0xb4, 0xd9, 0x63, 0x06, 0xba, 0x24, 0x36,
|
||||
0xd9, 0xa0, 0x7f, 0xa4, 0xff, 0x93, 0x90, 0x46, 0x27, 0xba, 0x5d, 0xbc, 0xe5, 0xfb, 0x7e, 0xe4,
|
||||
0x7b, 0x2f, 0x5f, 0x60, 0x29, 0x5a, 0x49, 0xca, 0x15, 0x96, 0xba, 0x9d, 0x14, 0x54, 0x98, 0x4e,
|
||||
0x3b, 0x5d, 0x0c, 0x66, 0xee, 0x05, 0x9e, 0xaf, 0x75, 0xbe, 0x91, 0xa2, 0xd3, 0xf9, 0x60, 0xa7,
|
||||
0x3b, 0x88, 0x2b, 0x7a, 0xdf, 0x92, 0x75, 0xc8, 0x21, 0x0e, 0x37, 0x39, 0x5b, 0xb0, 0xec, 0xb4,
|
||||
0xfa, 0x92, 0x98, 0xc0, 0x8c, 0xd4, 0xca, 0x68, 0xa9, 0x1c, 0x1f, 0x7b, 0xf4, 0xad, 0x71, 0x09,
|
||||
0x73, 0xa1, 0x95, 0x23, 0xe5, 0x5e, 0x5d, 0x6f, 0x88, 0x4f, 0x3c, 0x3f, 0x0b, 0xde, 0x73, 0x6f,
|
||||
0x08, 0x11, 0xa6, 0x8d, 0x5e, 0xf5, 0x7c, 0xba, 0x60, 0xd9, 0xbc, 0xf2, 0xe7, 0xf4, 0x0a, 0x66,
|
||||
0x15, 0x59, 0xa3, 0x95, 0xdd, 0x73, 0xf6, 0x83, 0xbf, 0x40, 0xfc, 0x40, 0xd6, 0xd6, 0x6b, 0xc2,
|
||||
0x0b, 0x38, 0x71, 0xda, 0x48, 0x11, 0xb6, 0x1a, 0xc4, 0x9f, 0xb9, 0xe3, 0xe3, 0x73, 0x27, 0xfb,
|
||||
0xdc, 0x9b, 0x0f, 0x06, 0x51, 0xe9, 0x9f, 0x8e, 0x77, 0x30, 0x2d, 0xeb, 0xb6, 0x45, 0x9e, 0xff,
|
||||
0x2a, 0x25, 0x0f, 0x8d, 0x24, 0x97, 0x07, 0xc8, 0xb0, 0x73, 0x3a, 0xc2, 0x12, 0xa2, 0x27, 0xd7,
|
||||
0x51, 0xbd, 0xf9, 0x67, 0x40, 0xc6, 0xae, 0x19, 0xde, 0x43, 0xfc, 0xb8, 0x6d, 0x5a, 0x69, 0xdf,
|
||||
0x0e, 0xa4, 0x84, 0x02, 0x92, 0xa3, 0x24, 0x1d, 0x35, 0x91, 0xff, 0xd7, 0xdb, 0xcf, 0x00, 0x00,
|
||||
0x00, 0xff, 0xff, 0xd6, 0x3f, 0xc3, 0xa1, 0xfc, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// ClientClient is the client API for Client service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type ClientClient interface {
|
||||
// Call allows a single request to be made
|
||||
Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
|
||||
// Stream is a bidirectional stream
|
||||
Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error)
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
|
||||
}
|
||||
|
||||
type clientClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewClientClient(cc *grpc.ClientConn) ClientClient {
|
||||
return &clientClient{cc}
|
||||
}
|
||||
|
||||
func (c *clientClient) Call(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
|
||||
out := new(Response)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Call", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clientClient) Stream(ctx context.Context, opts ...grpc.CallOption) (Client_StreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_Client_serviceDesc.Streams[0], "/go.micro.client.Client/Stream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &clientStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Client_StreamClient interface {
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type clientStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *clientStreamClient) Send(m *Request) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamClient) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *clientClient) Publish(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
|
||||
out := new(Message)
|
||||
err := c.cc.Invoke(ctx, "/go.micro.client.Client/Publish", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ClientServer is the server API for Client service.
|
||||
type ClientServer interface {
|
||||
// Call allows a single request to be made
|
||||
Call(context.Context, *Request) (*Response, error)
|
||||
// Stream is a bidirectional stream
|
||||
Stream(Client_StreamServer) error
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(context.Context, *Message) (*Message, error)
|
||||
}
|
||||
|
||||
// UnimplementedClientServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedClientServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedClientServer) Call(ctx context.Context, req *Request) (*Response, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Call not implemented")
|
||||
}
|
||||
func (*UnimplementedClientServer) Stream(srv Client_StreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Stream not implemented")
|
||||
}
|
||||
func (*UnimplementedClientServer) Publish(ctx context.Context, req *Message) (*Message, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented")
|
||||
}
|
||||
|
||||
func RegisterClientServer(s *grpc.Server, srv ClientServer) {
|
||||
s.RegisterService(&_Client_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Client_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Request)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ClientServer).Call(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.client.Client/Call",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClientServer).Call(ctx, req.(*Request))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Client_Stream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(ClientServer).Stream(&clientStreamServer{stream})
|
||||
}
|
||||
|
||||
type Client_StreamServer interface {
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type clientStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *clientStreamServer) Send(m *Response) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamServer) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _Client_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Message)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ClientServer).Publish(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/go.micro.client.Client/Publish",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClientServer).Publish(ctx, req.(*Message))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Client_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "go.micro.client.Client",
|
||||
HandlerType: (*ClientServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Call",
|
||||
Handler: _Client_Call_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Publish",
|
||||
Handler: _Client_Publish_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Stream",
|
||||
Handler: _Client_Stream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "client/service/proto/client.proto",
|
||||
}
|
@@ -1,215 +0,0 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: client/service/proto/client.proto
|
||||
|
||||
package go_micro_client
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/go-micro/v3/api"
|
||||
client "github.com/micro/go-micro/v3/client"
|
||||
server "github.com/micro/go-micro/v3/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for Client service
|
||||
|
||||
func NewClientEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Client service
|
||||
|
||||
type ClientService interface {
|
||||
// Call allows a single request to be made
|
||||
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
|
||||
// Stream is a bidirectional stream
|
||||
Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error)
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error)
|
||||
}
|
||||
|
||||
type clientService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewClientService(name string, c client.Client) ClientService {
|
||||
return &clientService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clientService) Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
|
||||
req := c.c.NewRequest(c.name, "Client.Call", in)
|
||||
out := new(Response)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clientService) Stream(ctx context.Context, opts ...client.CallOption) (Client_StreamService, error) {
|
||||
req := c.c.NewRequest(c.name, "Client.Stream", &Request{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &clientServiceStream{stream}, nil
|
||||
}
|
||||
|
||||
type Client_StreamService interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Request) error
|
||||
Recv() (*Response, error)
|
||||
}
|
||||
|
||||
type clientServiceStream struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *clientServiceStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *clientServiceStream) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *clientServiceStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *clientServiceStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *clientServiceStream) Send(m *Request) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *clientServiceStream) Recv() (*Response, error) {
|
||||
m := new(Response)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *clientService) Publish(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
|
||||
req := c.c.NewRequest(c.name, "Client.Publish", in)
|
||||
out := new(Message)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Client service
|
||||
|
||||
type ClientHandler interface {
|
||||
// Call allows a single request to be made
|
||||
Call(context.Context, *Request, *Response) error
|
||||
// Stream is a bidirectional stream
|
||||
Stream(context.Context, Client_StreamStream) error
|
||||
// Publish publishes a message and returns an empty Message
|
||||
Publish(context.Context, *Message, *Message) error
|
||||
}
|
||||
|
||||
func RegisterClientHandler(s server.Server, hdlr ClientHandler, opts ...server.HandlerOption) error {
|
||||
type client interface {
|
||||
Call(ctx context.Context, in *Request, out *Response) error
|
||||
Stream(ctx context.Context, stream server.Stream) error
|
||||
Publish(ctx context.Context, in *Message, out *Message) error
|
||||
}
|
||||
type Client struct {
|
||||
client
|
||||
}
|
||||
h := &clientHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Client{h}, opts...))
|
||||
}
|
||||
|
||||
type clientHandler struct {
|
||||
ClientHandler
|
||||
}
|
||||
|
||||
func (h *clientHandler) Call(ctx context.Context, in *Request, out *Response) error {
|
||||
return h.ClientHandler.Call(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *clientHandler) Stream(ctx context.Context, stream server.Stream) error {
|
||||
return h.ClientHandler.Stream(ctx, &clientStreamStream{stream})
|
||||
}
|
||||
|
||||
type Client_StreamStream interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Response) error
|
||||
Recv() (*Request, error)
|
||||
}
|
||||
|
||||
type clientStreamStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *clientStreamStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *clientStreamStream) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *clientStreamStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamStream) Send(m *Response) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *clientStreamStream) Recv() (*Request, error) {
|
||||
m := new(Request)
|
||||
if err := x.stream.Recv(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (h *clientHandler) Publish(ctx context.Context, in *Message, out *Message) error {
|
||||
return h.ClientHandler.Publish(ctx, in, out)
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package go.micro.client;
|
||||
|
||||
// Client is the micro client interface
|
||||
service Client {
|
||||
// Call allows a single request to be made
|
||||
rpc Call(Request) returns (Response) {};
|
||||
// Stream is a bidirectional stream
|
||||
rpc Stream(stream Request) returns (stream Response) {};
|
||||
// Publish publishes a message and returns an empty Message
|
||||
rpc Publish(Message) returns (Message) {};
|
||||
}
|
||||
|
||||
message Request {
|
||||
string service = 1;
|
||||
string endpoint = 2;
|
||||
string content_type = 3;
|
||||
bytes body = 4;
|
||||
}
|
||||
|
||||
message Response {
|
||||
bytes body = 1;
|
||||
}
|
||||
|
||||
message Message {
|
||||
string topic = 1;
|
||||
string content_type = 2;
|
||||
bytes body = 3;
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/v3/codec"
|
||||
)
|
||||
|
||||
type testRequest struct {
|
||||
service string
|
||||
method string
|
||||
endpoint string
|
||||
contentType string
|
||||
codec codec.Codec
|
||||
body interface{}
|
||||
opts RequestOptions
|
||||
}
|
||||
|
||||
func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
|
||||
var opts RequestOptions
|
||||
|
||||
for _, o := range reqOpts {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
// set the content-type specified
|
||||
if len(opts.ContentType) > 0 {
|
||||
contentType = opts.ContentType
|
||||
}
|
||||
|
||||
return &testRequest{
|
||||
service: service,
|
||||
method: endpoint,
|
||||
endpoint: endpoint,
|
||||
body: request,
|
||||
contentType: contentType,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *testRequest) ContentType() string {
|
||||
return r.contentType
|
||||
}
|
||||
|
||||
func (r *testRequest) Service() string {
|
||||
return r.service
|
||||
}
|
||||
|
||||
func (r *testRequest) Method() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
func (r *testRequest) Endpoint() string {
|
||||
return r.endpoint
|
||||
}
|
||||
|
||||
func (r *testRequest) Body() interface{} {
|
||||
return r.body
|
||||
}
|
||||
|
||||
func (r *testRequest) Codec() codec.Writer {
|
||||
return r.codec
|
||||
}
|
||||
|
||||
func (r *testRequest) Stream() bool {
|
||||
return r.opts.Stream
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/micro/go-micro/v3/errors"
|
||||
"github.com/micro/go-micro/v3/router"
|
||||
"github.com/micro/go-micro/v3/selector"
|
||||
pnet "github.com/micro/go-micro/v3/util/net"
|
||||
)
|
||||
|
||||
// LookupRoute for a request using the router and then choose one using the selector
|
||||
func LookupRoute(req Request, opts CallOptions) (*router.Route, error) {
|
||||
// check to see if the proxy has been set, if it has we don't need to lookup the routes; net.Proxy
|
||||
// returns a slice of addresses, so we'll use a random one. Eventually we should to use the
|
||||
// selector for this.
|
||||
service, addresses, _ := pnet.Proxy(req.Service(), opts.Address)
|
||||
if len(addresses) > 0 {
|
||||
return &router.Route{
|
||||
Service: service,
|
||||
Address: addresses[rand.Int()%len(addresses)],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// construct the router query
|
||||
query := []router.QueryOption{router.QueryService(req.Service())}
|
||||
|
||||
// if a custom network was requested, pass this to the router. By default the router will use it's
|
||||
// own network, which is set during initialisation.
|
||||
if len(opts.Network) > 0 {
|
||||
query = append(query, router.QueryNetwork(opts.Network))
|
||||
}
|
||||
|
||||
// lookup the routes which can be used to execute the request
|
||||
routes, err := opts.Router.Lookup(query...)
|
||||
if err == router.ErrRouteNotFound {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", req.Service(), err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", req.Service(), err.Error())
|
||||
}
|
||||
|
||||
// select the route to use for the request
|
||||
if route, err := opts.Selector.Select(routes, opts.SelectOptions...); err == selector.ErrNoneAvailable {
|
||||
return nil, errors.InternalServerError("go.micro.client", "service %s: %s", req.Service(), err.Error())
|
||||
} else if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", req.Service(), err.Error())
|
||||
} else {
|
||||
return route, nil
|
||||
}
|
||||
}
|
@@ -2,12 +2,10 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/v3/registry"
|
||||
)
|
||||
|
||||
// CallFunc represents the individual call func
|
||||
type CallFunc func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error
|
||||
type CallFunc func(ctx context.Context, addr string, req Request, rsp interface{}, opts CallOptions) error
|
||||
|
||||
// CallWrapper is a low level wrapper for the CallFunc
|
||||
type CallWrapper func(CallFunc) CallFunc
|
||||
|
@@ -1,50 +0,0 @@
|
||||
// Package cli is a urfave/cli implementation of the command
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/micro/go-micro/v3/cmd"
|
||||
)
|
||||
|
||||
type cliCmd struct {
|
||||
opts cmd.Options
|
||||
app *cli.App
|
||||
}
|
||||
|
||||
func (c *cliCmd) Init(opts ...cmd.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&c.opts)
|
||||
}
|
||||
c.app.Name = c.opts.Name
|
||||
c.app.Description = c.opts.Description
|
||||
c.app.Version = c.opts.Version
|
||||
c.app.Flags = c.opts.Flags
|
||||
c.app.Commands = c.opts.Commands
|
||||
c.app.Action = c.opts.Action
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cliCmd) Options() cmd.Options {
|
||||
return c.opts
|
||||
}
|
||||
|
||||
func (c *cliCmd) App() *cli.App {
|
||||
return c.app
|
||||
}
|
||||
|
||||
func (c *cliCmd) Run() error {
|
||||
return c.app.Run(os.Args)
|
||||
}
|
||||
|
||||
func (c *cliCmd) String() string {
|
||||
return "cli"
|
||||
}
|
||||
|
||||
func NewCmd(opts ...cmd.Option) cmd.Cmd {
|
||||
c := new(cliCmd)
|
||||
c.app = cli.NewApp()
|
||||
c.Init(opts...)
|
||||
return c
|
||||
}
|
86
cmd/cmd.go
86
cmd/cmd.go
@@ -1,86 +0,0 @@
|
||||
// Package cmd is an interface for building a command line binary
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
)
|
||||
|
||||
// TODO: replace App with RegisterCommand/RegisterFlags
|
||||
type Cmd interface {
|
||||
// Init initialises options
|
||||
// Note: Use Run to parse command line
|
||||
Init(opts ...Option) error
|
||||
// Options set within this command
|
||||
Options() Options
|
||||
// The cli app within this cmd
|
||||
App() *cli.App
|
||||
// Run executes the command
|
||||
Run() error
|
||||
// Implementation
|
||||
String() string
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type Options struct {
|
||||
// Name of the application
|
||||
Name string
|
||||
// Description of the application
|
||||
Description string
|
||||
// Version of the application
|
||||
Version string
|
||||
// Action to execute when Run is called and there is no subcommand
|
||||
// TODO replace with a build in context
|
||||
Action func(*cli.Context) error
|
||||
// TODO replace with built in command definition
|
||||
Commands []*cli.Command
|
||||
// TODO replace with built in flags definition
|
||||
Flags []cli.Flag
|
||||
// Other options for implementations of the interface
|
||||
// can be stored in a context
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// Command line Name
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
||||
// Command line Description
|
||||
func Description(d string) Option {
|
||||
return func(o *Options) {
|
||||
o.Description = d
|
||||
}
|
||||
}
|
||||
|
||||
// Command line Version
|
||||
func Version(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Version = v
|
||||
}
|
||||
}
|
||||
|
||||
// Commands to add
|
||||
func Commands(c ...*cli.Command) Option {
|
||||
return func(o *Options) {
|
||||
o.Commands = c
|
||||
}
|
||||
}
|
||||
|
||||
// Flags to add
|
||||
func Flags(f ...cli.Flag) Option {
|
||||
return func(o *Options) {
|
||||
o.Flags = f
|
||||
}
|
||||
}
|
||||
|
||||
// Action to execute
|
||||
func Action(a func(*cli.Context) error) Option {
|
||||
return func(o *Options) {
|
||||
o.Action = a
|
||||
}
|
||||
}
|
@@ -44,6 +44,8 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
var v []byte
|
||||
switch vb := b.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *Frame:
|
||||
v = vb.Data
|
||||
case *[]byte:
|
||||
|
53
codec/codec_test.go
Normal file
53
codec/codec_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package codec_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/v3/codec"
|
||||
"github.com/micro/go-micro/v3/codec/bytes"
|
||||
"github.com/micro/go-micro/v3/codec/grpc"
|
||||
"github.com/micro/go-micro/v3/codec/json"
|
||||
"github.com/micro/go-micro/v3/codec/jsonrpc"
|
||||
"github.com/micro/go-micro/v3/codec/proto"
|
||||
"github.com/micro/go-micro/v3/codec/protorpc"
|
||||
"github.com/micro/go-micro/v3/codec/text"
|
||||
)
|
||||
|
||||
type testRWC struct{}
|
||||
|
||||
func (rwc *testRWC) Read(p []byte) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (rwc *testRWC) Write(p []byte) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (rwc *testRWC) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCodecs(c io.ReadWriteCloser) map[string]codec.Codec {
|
||||
return map[string]codec.Codec{
|
||||
"bytes": bytes.NewCodec(c),
|
||||
"grpc": grpc.NewCodec(c),
|
||||
"json": json.NewCodec(c),
|
||||
"jsonrpc": jsonrpc.NewCodec(c),
|
||||
"proto": proto.NewCodec(c),
|
||||
"protorpc": protorpc.NewCodec(c),
|
||||
"text": text.NewCodec(c),
|
||||
}
|
||||
}
|
||||
|
||||
func Test_WriteEmptyBody(t *testing.T) {
|
||||
for name, c := range getCodecs(&testRWC{}) {
|
||||
err := c.Write(&codec.Message{
|
||||
Type: codec.Error,
|
||||
Header: map[string]string{},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("codec %s - expected no error when writing empty/nil body: %s", name, err)
|
||||
}
|
||||
}
|
||||
}
|
@@ -33,6 +33,10 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
}
|
||||
|
||||
func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
if b == nil {
|
||||
// Nothing to write
|
||||
return nil
|
||||
}
|
||||
p, ok := b.(proto.Message)
|
||||
if !ok {
|
||||
return codec.ErrInvalidMessage
|
||||
|
@@ -46,6 +46,8 @@ func (c *Codec) ReadBody(b interface{}) error {
|
||||
func (c *Codec) Write(m *codec.Message, b interface{}) error {
|
||||
var v []byte
|
||||
switch ve := b.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *Frame:
|
||||
v = ve.Data
|
||||
case *[]byte:
|
||||
|
101
config/config.go
101
config/config.go
@@ -2,97 +2,42 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/loader"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
"github.com/micro/go-micro/v3/config/source/file"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config is an interface abstraction for dynamic configuration
|
||||
type Config interface {
|
||||
// provide the reader.Values interface
|
||||
reader.Values
|
||||
// Init the config
|
||||
Init(opts ...Option) error
|
||||
// Options in the config
|
||||
Options() Options
|
||||
// Stop the config loader/watcher
|
||||
Close() error
|
||||
// Load config sources
|
||||
Load(source ...source.Source) error
|
||||
// Force a source changeset sync
|
||||
Sync() error
|
||||
// Watch a value for changes
|
||||
Watch(path ...string) (Watcher, error)
|
||||
Get(path string, options ...Option) (Value, error)
|
||||
Set(path string, val interface{}, options ...Option) error
|
||||
Delete(path string, options ...Option) error
|
||||
}
|
||||
|
||||
// Watcher is the config watcher
|
||||
type Watcher interface {
|
||||
Next() (reader.Value, error)
|
||||
Stop() error
|
||||
// Value represents a value of any type
|
||||
type Value interface {
|
||||
Exists() bool
|
||||
Bool(def bool) bool
|
||||
Int(def int) int
|
||||
String(def string) string
|
||||
Float64(def float64) float64
|
||||
Duration(def time.Duration) time.Duration
|
||||
StringSlice(def []string) []string
|
||||
StringMap(def map[string]string) map[string]string
|
||||
Scan(val interface{}) error
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Loader loader.Loader
|
||||
Reader reader.Reader
|
||||
Source []source.Source
|
||||
|
||||
// for alternative data
|
||||
Context context.Context
|
||||
Secret bool
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
var (
|
||||
// Default Config Manager
|
||||
DefaultConfig, _ = NewConfig()
|
||||
)
|
||||
|
||||
// NewConfig returns new config
|
||||
func NewConfig(opts ...Option) (Config, error) {
|
||||
return newConfig(opts...)
|
||||
func Secret(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.Secret = b
|
||||
}
|
||||
}
|
||||
|
||||
// Return config as raw json
|
||||
func Bytes() []byte {
|
||||
return DefaultConfig.Bytes()
|
||||
}
|
||||
|
||||
// Return config as a map
|
||||
func Map() map[string]interface{} {
|
||||
return DefaultConfig.Map()
|
||||
}
|
||||
|
||||
// Scan values to a go type
|
||||
func Scan(v interface{}) error {
|
||||
return DefaultConfig.Scan(v)
|
||||
}
|
||||
|
||||
// Force a source changeset sync
|
||||
func Sync() error {
|
||||
return DefaultConfig.Sync()
|
||||
}
|
||||
|
||||
// Get a value from the config
|
||||
func Get(path ...string) reader.Value {
|
||||
return DefaultConfig.Get(path...)
|
||||
}
|
||||
|
||||
// Load config sources
|
||||
func Load(source ...source.Source) error {
|
||||
return DefaultConfig.Load(source...)
|
||||
}
|
||||
|
||||
// Watch a value for changes
|
||||
func Watch(path ...string) (Watcher, error) {
|
||||
return DefaultConfig.Watch(path...)
|
||||
}
|
||||
|
||||
// LoadFile is short hand for creating a file source and loading it
|
||||
func LoadFile(path string) error {
|
||||
return Load(file.NewSource(
|
||||
file.WithPath(path),
|
||||
))
|
||||
type Secrets interface {
|
||||
Config
|
||||
}
|
||||
|
@@ -1,299 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/loader"
|
||||
"github.com/micro/go-micro/v3/config/loader/memory"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/reader/json"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
exit chan bool
|
||||
opts Options
|
||||
|
||||
sync.RWMutex
|
||||
// the current snapshot
|
||||
snap *loader.Snapshot
|
||||
// the current values
|
||||
vals reader.Values
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
lw loader.Watcher
|
||||
rd reader.Reader
|
||||
path []string
|
||||
value reader.Value
|
||||
}
|
||||
|
||||
func newConfig(opts ...Option) (Config, error) {
|
||||
var c config
|
||||
|
||||
c.Init(opts...)
|
||||
go c.run()
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *config) Init(opts ...Option) error {
|
||||
c.opts = Options{
|
||||
Reader: json.NewReader(),
|
||||
}
|
||||
c.exit = make(chan bool)
|
||||
for _, o := range opts {
|
||||
o(&c.opts)
|
||||
}
|
||||
|
||||
// default loader uses the configured reader
|
||||
if c.opts.Loader == nil {
|
||||
c.opts.Loader = memory.NewLoader(memory.WithReader(c.opts.Reader))
|
||||
}
|
||||
|
||||
err := c.opts.Loader.Load(c.opts.Source...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.snap, err = c.opts.Loader.Snapshot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.vals, err = c.opts.Reader.Values(c.snap.ChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *config) Options() Options {
|
||||
return c.opts
|
||||
}
|
||||
|
||||
func (c *config) run() {
|
||||
watch := func(w loader.Watcher) error {
|
||||
for {
|
||||
// get changeset
|
||||
snap, err := w.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
|
||||
if c.snap != nil && c.snap.Version >= snap.Version {
|
||||
c.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
// save
|
||||
c.snap = snap
|
||||
|
||||
// set values
|
||||
c.vals, _ = c.opts.Reader.Values(snap.ChangeSet)
|
||||
|
||||
c.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
w, err := c.opts.Loader.Watch()
|
||||
if err != nil {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
// the stop watch func
|
||||
go func() {
|
||||
select {
|
||||
case <-done:
|
||||
case <-c.exit:
|
||||
}
|
||||
w.Stop()
|
||||
}()
|
||||
|
||||
// block watch
|
||||
if err := watch(w); err != nil {
|
||||
// do something better
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
// close done chan
|
||||
close(done)
|
||||
|
||||
// if the config is closed exit
|
||||
select {
|
||||
case <-c.exit:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *config) Map() map[string]interface{} {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.vals.Map()
|
||||
}
|
||||
|
||||
func (c *config) Scan(v interface{}) error {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.vals.Scan(v)
|
||||
}
|
||||
|
||||
// sync loads all the sources, calls the parser and updates the config
|
||||
func (c *config) Sync() error {
|
||||
if err := c.opts.Loader.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
snap, err := c.opts.Loader.Snapshot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.snap = snap
|
||||
vals, err := c.opts.Reader.Values(snap.ChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.vals = vals
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *config) Close() error {
|
||||
select {
|
||||
case <-c.exit:
|
||||
return nil
|
||||
default:
|
||||
close(c.exit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *config) Get(path ...string) reader.Value {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
// did sync actually work?
|
||||
if c.vals != nil {
|
||||
return c.vals.Get(path...)
|
||||
}
|
||||
|
||||
// no value
|
||||
return newValue()
|
||||
}
|
||||
|
||||
func (c *config) Set(val interface{}, path ...string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.vals != nil {
|
||||
c.vals.Set(val, path...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *config) Del(path ...string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.vals != nil {
|
||||
c.vals.Del(path...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *config) Bytes() []byte {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
if c.vals == nil {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
return c.vals.Bytes()
|
||||
}
|
||||
|
||||
func (c *config) Load(sources ...source.Source) error {
|
||||
if err := c.opts.Loader.Load(sources...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
snap, err := c.opts.Loader.Snapshot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.snap = snap
|
||||
vals, err := c.opts.Reader.Values(snap.ChangeSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.vals = vals
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *config) Watch(path ...string) (Watcher, error) {
|
||||
value := c.Get(path...)
|
||||
|
||||
w, err := c.opts.Loader.Watch(path...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &watcher{
|
||||
lw: w,
|
||||
rd: c.opts.Reader,
|
||||
path: path,
|
||||
value: value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *config) String() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (reader.Value, error) {
|
||||
for {
|
||||
s, err := w.lw.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only process changes
|
||||
if bytes.Equal(w.value.Bytes(), s.ChangeSet.Data) {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := w.rd.Values(s.ChangeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.value = v.Get()
|
||||
return w.value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
return w.lw.Stop()
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
"github.com/micro/go-micro/v3/config/source/env"
|
||||
"github.com/micro/go-micro/v3/config/source/file"
|
||||
"github.com/micro/go-micro/v3/config/source/memory"
|
||||
)
|
||||
|
||||
func createFileForIssue18(t *testing.T, content string) *os.File {
|
||||
data := []byte(content)
|
||||
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
|
||||
fh, err := os.Create(path)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = fh.Write(data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
return fh
|
||||
}
|
||||
|
||||
func createFileForTest(t *testing.T) *os.File {
|
||||
data := []byte(`{"foo": "bar"}`)
|
||||
path := filepath.Join(os.TempDir(), fmt.Sprintf("file.%d", time.Now().UnixNano()))
|
||||
fh, err := os.Create(path)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = fh.Write(data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
return fh
|
||||
}
|
||||
|
||||
func TestConfigLoadWithGoodFile(t *testing.T) {
|
||||
fh := createFileForTest(t)
|
||||
path := fh.Name()
|
||||
defer func() {
|
||||
fh.Close()
|
||||
os.Remove(path)
|
||||
}()
|
||||
|
||||
// Create new config
|
||||
conf, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
// Load file source
|
||||
if err := conf.Load(file.NewSource(
|
||||
file.WithPath(path),
|
||||
)); err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLoadWithInvalidFile(t *testing.T) {
|
||||
fh := createFileForTest(t)
|
||||
path := fh.Name()
|
||||
defer func() {
|
||||
fh.Close()
|
||||
os.Remove(path)
|
||||
}()
|
||||
|
||||
// Create new config
|
||||
conf, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
// Load file source
|
||||
err = conf.Load(file.NewSource(
|
||||
file.WithPath(path),
|
||||
file.WithPath("/i/do/not/exists.json"),
|
||||
))
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Expected error but none !")
|
||||
}
|
||||
if !strings.Contains(fmt.Sprintf("%v", err), "/i/do/not/exists.json") {
|
||||
t.Fatalf("Expected error to contain the unexisting file but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigMerge(t *testing.T) {
|
||||
fh := createFileForIssue18(t, `{
|
||||
"amqp": {
|
||||
"host": "rabbit.platform",
|
||||
"port": 80
|
||||
},
|
||||
"handler": {
|
||||
"exchange": "springCloudBus"
|
||||
}
|
||||
}`)
|
||||
path := fh.Name()
|
||||
defer func() {
|
||||
fh.Close()
|
||||
os.Remove(path)
|
||||
}()
|
||||
os.Setenv("AMQP_HOST", "rabbit.testing.com")
|
||||
|
||||
conf, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
if err := conf.Load(
|
||||
file.NewSource(
|
||||
file.WithPath(path),
|
||||
),
|
||||
env.NewSource(),
|
||||
); err != nil {
|
||||
t.Fatalf("Expected no error but got %v", err)
|
||||
}
|
||||
|
||||
actualHost := conf.Get("amqp", "host").String("backup")
|
||||
if actualHost != "rabbit.testing.com" {
|
||||
t.Fatalf("Expected %v but got %v",
|
||||
"rabbit.testing.com",
|
||||
actualHost)
|
||||
}
|
||||
}
|
||||
|
||||
func equalS(t *testing.T, actual, expect string) {
|
||||
if actual != expect {
|
||||
t.Errorf("Expected %s but got %s", actual, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigWatcherDirtyOverrite(t *testing.T) {
|
||||
n := runtime.GOMAXPROCS(0)
|
||||
defer runtime.GOMAXPROCS(n)
|
||||
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
l := 100
|
||||
|
||||
ss := make([]source.Source, l, l)
|
||||
|
||||
for i := 0; i < l; i++ {
|
||||
ss[i] = memory.NewSource(memory.WithJSON([]byte(fmt.Sprintf(`{"key%d": "val%d"}`, i, i))))
|
||||
}
|
||||
|
||||
conf, _ := NewConfig()
|
||||
|
||||
for _, s := range ss {
|
||||
_ = conf.Load(s)
|
||||
}
|
||||
runtime.Gosched()
|
||||
|
||||
for i, _ := range ss {
|
||||
k := fmt.Sprintf("key%d", i)
|
||||
v := fmt.Sprintf("val%d", i)
|
||||
equalS(t, conf.Get(k).String(""), v)
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
// Package encoder handles source encoding formats
|
||||
package encoder
|
||||
|
||||
type Encoder interface {
|
||||
Encode(interface{}) ([]byte, error)
|
||||
Decode([]byte, interface{}) error
|
||||
String() string
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
package hcl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
)
|
||||
|
||||
type hclEncoder struct{}
|
||||
|
||||
func (h hclEncoder) Encode(v interface{}) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (h hclEncoder) Decode(d []byte, v interface{}) error {
|
||||
return hcl.Unmarshal(d, v)
|
||||
}
|
||||
|
||||
func (h hclEncoder) String() string {
|
||||
return "hcl"
|
||||
}
|
||||
|
||||
func NewEncoder() encoder.Encoder {
|
||||
return hclEncoder{}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
)
|
||||
|
||||
type jsonEncoder struct{}
|
||||
|
||||
func (j jsonEncoder) Encode(v interface{}) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (j jsonEncoder) Decode(d []byte, v interface{}) error {
|
||||
return json.Unmarshal(d, v)
|
||||
}
|
||||
|
||||
func (j jsonEncoder) String() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
func NewEncoder() encoder.Encoder {
|
||||
return jsonEncoder{}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
)
|
||||
|
||||
type tomlEncoder struct{}
|
||||
|
||||
func (t tomlEncoder) Encode(v interface{}) ([]byte, error) {
|
||||
b := bytes.NewBuffer(nil)
|
||||
defer b.Reset()
|
||||
err := toml.NewEncoder(b).Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func (t tomlEncoder) Decode(d []byte, v interface{}) error {
|
||||
return toml.Unmarshal(d, v)
|
||||
}
|
||||
|
||||
func (t tomlEncoder) String() string {
|
||||
return "toml"
|
||||
}
|
||||
|
||||
func NewEncoder() encoder.Encoder {
|
||||
return tomlEncoder{}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
package xml
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
)
|
||||
|
||||
type xmlEncoder struct{}
|
||||
|
||||
func (x xmlEncoder) Encode(v interface{}) ([]byte, error) {
|
||||
return xml.Marshal(v)
|
||||
}
|
||||
|
||||
func (x xmlEncoder) Decode(d []byte, v interface{}) error {
|
||||
return xml.Unmarshal(d, v)
|
||||
}
|
||||
|
||||
func (x xmlEncoder) String() string {
|
||||
return "xml"
|
||||
}
|
||||
|
||||
func NewEncoder() encoder.Encoder {
|
||||
return xmlEncoder{}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
)
|
||||
|
||||
type yamlEncoder struct{}
|
||||
|
||||
func (y yamlEncoder) Encode(v interface{}) ([]byte, error) {
|
||||
return yaml.Marshal(v)
|
||||
}
|
||||
|
||||
func (y yamlEncoder) Decode(d []byte, v interface{}) error {
|
||||
return yaml.Unmarshal(d, v)
|
||||
}
|
||||
|
||||
func (y yamlEncoder) String() string {
|
||||
return "yaml"
|
||||
}
|
||||
|
||||
func NewEncoder() encoder.Encoder {
|
||||
return yamlEncoder{}
|
||||
}
|
48
config/env/env.go
vendored
Normal file
48
config/env/env.go
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Package env provides config from environment variables
|
||||
package env
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/micro/go-micro/v3/config"
|
||||
)
|
||||
|
||||
type envConfig struct{}
|
||||
|
||||
// NewConfig returns new config
|
||||
func NewConfig() (*envConfig, error) {
|
||||
return new(envConfig), nil
|
||||
}
|
||||
|
||||
func formatKey(v string) string {
|
||||
if len(v) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
v = strings.ToUpper(v)
|
||||
return strings.Replace(v, ".", "_", -1)
|
||||
}
|
||||
|
||||
func (c *envConfig) Get(path string, options ...config.Option) (config.Value, error) {
|
||||
v := os.Getenv(formatKey(path))
|
||||
if len(v) == 0 {
|
||||
v = "{}"
|
||||
}
|
||||
return config.NewJSONValue([]byte(v)), nil
|
||||
}
|
||||
|
||||
func (c *envConfig) Set(path string, val interface{}, options ...config.Option) error {
|
||||
key := formatKey(path)
|
||||
v, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Setenv(key, string(v))
|
||||
}
|
||||
|
||||
func (c *envConfig) Delete(path string, options ...config.Option) error {
|
||||
v := formatKey(path)
|
||||
return os.Unsetenv(v)
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
// package loader manages loading from multiple sources
|
||||
package loader
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
// Loader manages loading sources
|
||||
type Loader interface {
|
||||
// Stop the loader
|
||||
Close() error
|
||||
// Load the sources
|
||||
Load(...source.Source) error
|
||||
// A Snapshot of loaded config
|
||||
Snapshot() (*Snapshot, error)
|
||||
// Force sync of sources
|
||||
Sync() error
|
||||
// Watch for changes
|
||||
Watch(...string) (Watcher, error)
|
||||
// Name of loader
|
||||
String() string
|
||||
}
|
||||
|
||||
// Watcher lets you watch sources and returns a merged ChangeSet
|
||||
type Watcher interface {
|
||||
// First call to next may return the current Snapshot
|
||||
// If you are watching a path then only the data from
|
||||
// that path is returned.
|
||||
Next() (*Snapshot, error)
|
||||
// Stop watching for changes
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// Snapshot is a merged ChangeSet
|
||||
type Snapshot struct {
|
||||
// The merged ChangeSet
|
||||
ChangeSet *source.ChangeSet
|
||||
// Deterministic and comparable version of the snapshot
|
||||
Version string
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Reader reader.Reader
|
||||
Source []source.Source
|
||||
|
||||
// for alternative data
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
// Copy snapshot
|
||||
func Copy(s *Snapshot) *Snapshot {
|
||||
cs := *(s.ChangeSet)
|
||||
|
||||
return &Snapshot{
|
||||
ChangeSet: &cs,
|
||||
Version: s.Version,
|
||||
}
|
||||
}
|
@@ -1,454 +0,0 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/loader"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/reader/json"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
type memory struct {
|
||||
exit chan bool
|
||||
opts loader.Options
|
||||
|
||||
sync.RWMutex
|
||||
// the current snapshot
|
||||
snap *loader.Snapshot
|
||||
// the current values
|
||||
vals reader.Values
|
||||
// all the sets
|
||||
sets []*source.ChangeSet
|
||||
// all the sources
|
||||
sources []source.Source
|
||||
|
||||
watchers *list.List
|
||||
}
|
||||
|
||||
type updateValue struct {
|
||||
version string
|
||||
value reader.Value
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
exit chan bool
|
||||
path []string
|
||||
value reader.Value
|
||||
reader reader.Reader
|
||||
version string
|
||||
updates chan updateValue
|
||||
}
|
||||
|
||||
func (m *memory) watch(idx int, s source.Source) {
|
||||
// watches a source for changes
|
||||
watch := func(idx int, s source.Watcher) error {
|
||||
for {
|
||||
// get changeset
|
||||
cs, err := s.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
|
||||
// save
|
||||
m.sets[idx] = cs
|
||||
|
||||
// merge sets
|
||||
set, err := m.opts.Reader.Merge(m.sets...)
|
||||
if err != nil {
|
||||
m.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// set values
|
||||
m.vals, _ = m.opts.Reader.Values(set)
|
||||
m.snap = &loader.Snapshot{
|
||||
ChangeSet: set,
|
||||
Version: genVer(),
|
||||
}
|
||||
m.Unlock()
|
||||
|
||||
// send watch updates
|
||||
m.update()
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
// watch the source
|
||||
w, err := s.Watch()
|
||||
if err != nil {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
// the stop watch func
|
||||
go func() {
|
||||
select {
|
||||
case <-done:
|
||||
case <-m.exit:
|
||||
}
|
||||
w.Stop()
|
||||
}()
|
||||
|
||||
// block watch
|
||||
if err := watch(idx, w); err != nil {
|
||||
// do something better
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
// close done chan
|
||||
close(done)
|
||||
|
||||
// if the config is closed exit
|
||||
select {
|
||||
case <-m.exit:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *memory) loaded() bool {
|
||||
var loaded bool
|
||||
m.RLock()
|
||||
if m.vals != nil {
|
||||
loaded = true
|
||||
}
|
||||
m.RUnlock()
|
||||
return loaded
|
||||
}
|
||||
|
||||
// reload reads the sets and creates new values
|
||||
func (m *memory) reload() error {
|
||||
m.Lock()
|
||||
|
||||
// merge sets
|
||||
set, err := m.opts.Reader.Merge(m.sets...)
|
||||
if err != nil {
|
||||
m.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// set values
|
||||
m.vals, _ = m.opts.Reader.Values(set)
|
||||
m.snap = &loader.Snapshot{
|
||||
ChangeSet: set,
|
||||
Version: genVer(),
|
||||
}
|
||||
|
||||
m.Unlock()
|
||||
|
||||
// update watchers
|
||||
m.update()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memory) update() {
|
||||
watchers := make([]*watcher, 0, m.watchers.Len())
|
||||
|
||||
m.RLock()
|
||||
for e := m.watchers.Front(); e != nil; e = e.Next() {
|
||||
watchers = append(watchers, e.Value.(*watcher))
|
||||
}
|
||||
|
||||
vals := m.vals
|
||||
snap := m.snap
|
||||
m.RUnlock()
|
||||
|
||||
for _, w := range watchers {
|
||||
if w.version >= snap.Version {
|
||||
continue
|
||||
}
|
||||
|
||||
uv := updateValue{
|
||||
version: m.snap.Version,
|
||||
value: vals.Get(w.path...),
|
||||
}
|
||||
|
||||
select {
|
||||
case w.updates <- uv:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot returns a snapshot of the current loaded config
|
||||
func (m *memory) Snapshot() (*loader.Snapshot, error) {
|
||||
if m.loaded() {
|
||||
m.RLock()
|
||||
snap := loader.Copy(m.snap)
|
||||
m.RUnlock()
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
// not loaded, sync
|
||||
if err := m.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// make copy
|
||||
m.RLock()
|
||||
snap := loader.Copy(m.snap)
|
||||
m.RUnlock()
|
||||
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
// Sync loads all the sources, calls the parser and updates the config
|
||||
func (m *memory) Sync() error {
|
||||
//nolint:prealloc
|
||||
var sets []*source.ChangeSet
|
||||
|
||||
m.Lock()
|
||||
|
||||
// read the source
|
||||
var gerr []string
|
||||
|
||||
for _, source := range m.sources {
|
||||
ch, err := source.Read()
|
||||
if err != nil {
|
||||
gerr = append(gerr, err.Error())
|
||||
continue
|
||||
}
|
||||
sets = append(sets, ch)
|
||||
}
|
||||
|
||||
// merge sets
|
||||
set, err := m.opts.Reader.Merge(sets...)
|
||||
if err != nil {
|
||||
m.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// set values
|
||||
vals, err := m.opts.Reader.Values(set)
|
||||
if err != nil {
|
||||
m.Unlock()
|
||||
return err
|
||||
}
|
||||
m.vals = vals
|
||||
m.snap = &loader.Snapshot{
|
||||
ChangeSet: set,
|
||||
Version: genVer(),
|
||||
}
|
||||
|
||||
m.Unlock()
|
||||
|
||||
// update watchers
|
||||
m.update()
|
||||
|
||||
if len(gerr) > 0 {
|
||||
return fmt.Errorf("source loading errors: %s", strings.Join(gerr, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memory) Close() error {
|
||||
select {
|
||||
case <-m.exit:
|
||||
return nil
|
||||
default:
|
||||
close(m.exit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memory) Get(path ...string) (reader.Value, error) {
|
||||
if !m.loaded() {
|
||||
if err := m.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// did sync actually work?
|
||||
if m.vals != nil {
|
||||
return m.vals.Get(path...), nil
|
||||
}
|
||||
|
||||
// assuming vals is nil
|
||||
// create new vals
|
||||
|
||||
ch := m.snap.ChangeSet
|
||||
|
||||
// we are truly screwed, trying to load in a hacked way
|
||||
v, err := m.opts.Reader.Values(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// lets set it just because
|
||||
m.vals = v
|
||||
|
||||
if m.vals != nil {
|
||||
return m.vals.Get(path...), nil
|
||||
}
|
||||
|
||||
// ok we're going hardcore now
|
||||
|
||||
return nil, errors.New("no values")
|
||||
}
|
||||
|
||||
func (m *memory) Load(sources ...source.Source) error {
|
||||
var gerrors []string
|
||||
|
||||
for _, source := range sources {
|
||||
set, err := source.Read()
|
||||
if err != nil {
|
||||
gerrors = append(gerrors,
|
||||
fmt.Sprintf("error loading source %s: %v",
|
||||
source,
|
||||
err))
|
||||
// continue processing
|
||||
continue
|
||||
}
|
||||
m.Lock()
|
||||
m.sources = append(m.sources, source)
|
||||
m.sets = append(m.sets, set)
|
||||
idx := len(m.sets) - 1
|
||||
m.Unlock()
|
||||
go m.watch(idx, source)
|
||||
}
|
||||
|
||||
if err := m.reload(); err != nil {
|
||||
gerrors = append(gerrors, err.Error())
|
||||
}
|
||||
|
||||
// Return errors
|
||||
if len(gerrors) != 0 {
|
||||
return errors.New(strings.Join(gerrors, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memory) Watch(path ...string) (loader.Watcher, error) {
|
||||
value, err := m.Get(path...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
|
||||
w := &watcher{
|
||||
exit: make(chan bool),
|
||||
path: path,
|
||||
value: value,
|
||||
reader: m.opts.Reader,
|
||||
updates: make(chan updateValue, 1),
|
||||
version: m.snap.Version,
|
||||
}
|
||||
|
||||
e := m.watchers.PushBack(w)
|
||||
|
||||
m.Unlock()
|
||||
|
||||
go func() {
|
||||
<-w.exit
|
||||
m.Lock()
|
||||
m.watchers.Remove(e)
|
||||
m.Unlock()
|
||||
}()
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (m *memory) String() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*loader.Snapshot, error) {
|
||||
update := func(v reader.Value) *loader.Snapshot {
|
||||
w.value = v
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Data: v.Bytes(),
|
||||
Format: w.reader.String(),
|
||||
Source: "memory",
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
return &loader.Snapshot{
|
||||
ChangeSet: cs,
|
||||
Version: w.version,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil, errors.New("watcher stopped")
|
||||
|
||||
case uv := <-w.updates:
|
||||
if uv.version <= w.version {
|
||||
continue
|
||||
}
|
||||
|
||||
v := uv.value
|
||||
|
||||
w.version = uv.version
|
||||
|
||||
if bytes.Equal(w.value.Bytes(), v.Bytes()) {
|
||||
continue
|
||||
}
|
||||
|
||||
return update(v), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
select {
|
||||
case <-w.exit:
|
||||
default:
|
||||
close(w.exit)
|
||||
close(w.updates)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func genVer() string {
|
||||
return fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func NewLoader(opts ...loader.Option) loader.Loader {
|
||||
options := loader.Options{
|
||||
Reader: json.NewReader(),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
m := &memory{
|
||||
exit: make(chan bool),
|
||||
opts: options,
|
||||
watchers: list.New(),
|
||||
sources: options.Source,
|
||||
}
|
||||
|
||||
m.sets = make([]*source.ChangeSet, len(options.Source))
|
||||
|
||||
for i, s := range options.Source {
|
||||
m.sets[i] = &source.ChangeSet{Source: s.String()}
|
||||
go m.watch(i, s)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/v3/config/loader"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
// WithSource appends a source to list of sources
|
||||
func WithSource(s source.Source) loader.Option {
|
||||
return func(o *loader.Options) {
|
||||
o.Source = append(o.Source, s)
|
||||
}
|
||||
}
|
||||
|
||||
// WithReader sets the config reader
|
||||
func WithReader(r reader.Reader) loader.Option {
|
||||
return func(o *loader.Options) {
|
||||
o.Reader = r
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/v3/config/loader"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
// WithLoader sets the loader for manager config
|
||||
func WithLoader(l loader.Loader) Option {
|
||||
return func(o *Options) {
|
||||
o.Loader = l
|
||||
}
|
||||
}
|
||||
|
||||
// WithSource appends a source to list of sources
|
||||
func WithSource(s source.Source) Option {
|
||||
return func(o *Options) {
|
||||
o.Source = append(o.Source, s)
|
||||
}
|
||||
}
|
||||
|
||||
// WithReader sets the config reader
|
||||
func WithReader(r reader.Reader) Option {
|
||||
return func(o *Options) {
|
||||
o.Reader = r
|
||||
}
|
||||
}
|
@@ -1,83 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
"github.com/micro/go-micro/v3/config/encoder/json"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
type jsonReader struct {
|
||||
opts reader.Options
|
||||
json encoder.Encoder
|
||||
}
|
||||
|
||||
func (j *jsonReader) Merge(changes ...*source.ChangeSet) (*source.ChangeSet, error) {
|
||||
var merged map[string]interface{}
|
||||
|
||||
for _, m := range changes {
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(m.Data) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
codec, ok := j.opts.Encoding[m.Format]
|
||||
if !ok {
|
||||
// fallback
|
||||
codec = j.json
|
||||
}
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := codec.Decode(m.Data, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := mergo.Map(&merged, data, mergo.WithOverride); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b, err := j.json.Encode(merged)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Timestamp: time.Now(),
|
||||
Data: b,
|
||||
Source: "json",
|
||||
Format: j.json.String(),
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
func (j *jsonReader) Values(ch *source.ChangeSet) (reader.Values, error) {
|
||||
if ch == nil {
|
||||
return nil, errors.New("changeset is nil")
|
||||
}
|
||||
if ch.Format != "json" {
|
||||
return nil, errors.New("unsupported format")
|
||||
}
|
||||
return newValues(ch, j.opts)
|
||||
}
|
||||
|
||||
func (j *jsonReader) String() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
// NewReader creates a json reader
|
||||
func NewReader(opts ...reader.Option) reader.Reader {
|
||||
options := reader.NewOptions(opts...)
|
||||
return &jsonReader{
|
||||
json: json.NewEncoder(),
|
||||
opts: options,
|
||||
}
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
data := []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`)
|
||||
|
||||
testData := []struct {
|
||||
path []string
|
||||
value string
|
||||
}{
|
||||
{
|
||||
[]string{"foo"},
|
||||
"bar",
|
||||
},
|
||||
{
|
||||
[]string{"baz", "bar"},
|
||||
"cat",
|
||||
},
|
||||
}
|
||||
|
||||
values := newTestValues(t, data)
|
||||
|
||||
for _, test := range testData {
|
||||
if v := values.Get(test.path...).String(""); v != test.value {
|
||||
t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableReplaceEnvVars(t *testing.T) {
|
||||
data := []byte(`{"foo": "bar", "baz": {"bar": "test/${test}"}}`)
|
||||
|
||||
tests := []struct {
|
||||
path []string
|
||||
value string
|
||||
opts []reader.Option
|
||||
}{
|
||||
{
|
||||
[]string{"baz", "bar"},
|
||||
"test/",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]string{"baz", "bar"},
|
||||
"test/${test}",
|
||||
[]reader.Option{reader.WithDisableReplaceEnvVars()},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
values := newTestValues(t, data, test.opts...)
|
||||
|
||||
if v := values.Get(test.path...).String(""); v != test.value {
|
||||
t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newTestValues(t *testing.T, data []byte, opts ...reader.Option) reader.Values {
|
||||
r := NewReader(opts...)
|
||||
|
||||
c, err := r.Merge(&source.ChangeSet{Data: data}, &source.ChangeSet{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
values, err := r.Values(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
@@ -1,206 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
simple "github.com/bitly/go-simplejson"
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
type jsonValues struct {
|
||||
ch *source.ChangeSet
|
||||
sj *simple.Json
|
||||
}
|
||||
|
||||
type jsonValue struct {
|
||||
*simple.Json
|
||||
}
|
||||
|
||||
func newValues(ch *source.ChangeSet, opts reader.Options) (reader.Values, error) {
|
||||
sj := simple.New()
|
||||
data := ch.Data
|
||||
|
||||
if !opts.DisableReplaceEnvVars {
|
||||
data, _ = reader.ReplaceEnvVars(ch.Data)
|
||||
}
|
||||
|
||||
if err := sj.UnmarshalJSON(data); err != nil {
|
||||
sj.SetPath(nil, string(ch.Data))
|
||||
}
|
||||
return &jsonValues{ch, sj}, nil
|
||||
}
|
||||
|
||||
func (j *jsonValues) Get(path ...string) reader.Value {
|
||||
return &jsonValue{j.sj.GetPath(path...)}
|
||||
}
|
||||
|
||||
func (j *jsonValues) Del(path ...string) {
|
||||
// delete the tree?
|
||||
if len(path) == 0 {
|
||||
j.sj = simple.New()
|
||||
return
|
||||
}
|
||||
|
||||
if len(path) == 1 {
|
||||
j.sj.Del(path[0])
|
||||
return
|
||||
}
|
||||
|
||||
vals := j.sj.GetPath(path[:len(path)-1]...)
|
||||
vals.Del(path[len(path)-1])
|
||||
j.sj.SetPath(path[:len(path)-1], vals.Interface())
|
||||
return
|
||||
}
|
||||
|
||||
func (j *jsonValues) Set(val interface{}, path ...string) {
|
||||
j.sj.SetPath(path, val)
|
||||
}
|
||||
|
||||
func (j *jsonValues) Bytes() []byte {
|
||||
b, _ := j.sj.MarshalJSON()
|
||||
return b
|
||||
}
|
||||
|
||||
func (j *jsonValues) Map() map[string]interface{} {
|
||||
m, _ := j.sj.Map()
|
||||
return m
|
||||
}
|
||||
|
||||
func (j *jsonValues) Scan(v interface{}) error {
|
||||
b, err := j.sj.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
func (j *jsonValues) String() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
func (j *jsonValue) Bool(def bool) bool {
|
||||
b, err := j.Json.Bool()
|
||||
if err == nil {
|
||||
return b
|
||||
}
|
||||
|
||||
str, ok := j.Interface().(string)
|
||||
if !ok {
|
||||
return def
|
||||
}
|
||||
|
||||
b, err = strconv.ParseBool(str)
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (j *jsonValue) Int(def int) int {
|
||||
i, err := j.Json.Int()
|
||||
if err == nil {
|
||||
return i
|
||||
}
|
||||
|
||||
str, ok := j.Interface().(string)
|
||||
if !ok {
|
||||
return def
|
||||
}
|
||||
|
||||
i, err = strconv.Atoi(str)
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func (j *jsonValue) String(def string) string {
|
||||
return j.Json.MustString(def)
|
||||
}
|
||||
|
||||
func (j *jsonValue) Float64(def float64) float64 {
|
||||
f, err := j.Json.Float64()
|
||||
if err == nil {
|
||||
return f
|
||||
}
|
||||
|
||||
str, ok := j.Interface().(string)
|
||||
if !ok {
|
||||
return def
|
||||
}
|
||||
|
||||
f, err = strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (j *jsonValue) Duration(def time.Duration) time.Duration {
|
||||
v, err := j.Json.String()
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
|
||||
value, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (j *jsonValue) StringSlice(def []string) []string {
|
||||
v, err := j.Json.String()
|
||||
if err == nil {
|
||||
sl := strings.Split(v, ",")
|
||||
if len(sl) > 1 {
|
||||
return sl
|
||||
}
|
||||
}
|
||||
return j.Json.MustStringArray(def)
|
||||
}
|
||||
|
||||
func (j *jsonValue) StringMap(def map[string]string) map[string]string {
|
||||
m, err := j.Json.Map()
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
|
||||
res := map[string]string{}
|
||||
|
||||
for k, v := range m {
|
||||
res[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (j *jsonValue) Scan(v interface{}) error {
|
||||
b, err := j.Json.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
func (j *jsonValue) Bytes() []byte {
|
||||
b, err := j.Json.Bytes()
|
||||
if err != nil {
|
||||
// try return marshalled
|
||||
b, err = j.Json.MarshalJSON()
|
||||
if err != nil {
|
||||
return []byte{}
|
||||
}
|
||||
return b
|
||||
}
|
||||
return b
|
||||
}
|
@@ -1,86 +0,0 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/reader"
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
func TestValues(t *testing.T) {
|
||||
emptyStr := ""
|
||||
testData := []struct {
|
||||
csdata []byte
|
||||
path []string
|
||||
accepter interface{}
|
||||
value interface{}
|
||||
}{
|
||||
{
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "cat"}}`),
|
||||
[]string{"foo"},
|
||||
emptyStr,
|
||||
"bar",
|
||||
},
|
||||
{
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "cat"}}`),
|
||||
[]string{"baz", "bar"},
|
||||
emptyStr,
|
||||
"cat",
|
||||
},
|
||||
}
|
||||
|
||||
for idx, test := range testData {
|
||||
values, err := newValues(&source.ChangeSet{
|
||||
Data: test.csdata,
|
||||
}, reader.Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = values.Get(test.path...).Scan(&test.accepter)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test.accepter != test.value {
|
||||
t.Fatalf("No.%d Expected %v got %v for path %v", idx, test.value, test.accepter, test.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructArray(t *testing.T) {
|
||||
type T struct {
|
||||
Foo string
|
||||
}
|
||||
|
||||
emptyTSlice := []T{}
|
||||
|
||||
testData := []struct {
|
||||
csdata []byte
|
||||
accepter []T
|
||||
value []T
|
||||
}{
|
||||
{
|
||||
[]byte(`[{"foo": "bar"}]`),
|
||||
emptyTSlice,
|
||||
[]T{{Foo: "bar"}},
|
||||
},
|
||||
}
|
||||
|
||||
for idx, test := range testData {
|
||||
values, err := newValues(&source.ChangeSet{
|
||||
Data: test.csdata,
|
||||
}, reader.Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = values.Get().Scan(&test.accepter)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(test.accepter, test.value) {
|
||||
t.Fatalf("No.%d Expected %v got %v", idx, test.value, test.accepter)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"github.com/micro/go-micro/v3/config/encoder"
|
||||
"github.com/micro/go-micro/v3/config/encoder/hcl"
|
||||
"github.com/micro/go-micro/v3/config/encoder/json"
|
||||
"github.com/micro/go-micro/v3/config/encoder/toml"
|
||||
"github.com/micro/go-micro/v3/config/encoder/xml"
|
||||
"github.com/micro/go-micro/v3/config/encoder/yaml"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Encoding map[string]encoder.Encoder
|
||||
DisableReplaceEnvVars bool
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Encoding: map[string]encoder.Encoder{
|
||||
"json": json.NewEncoder(),
|
||||
"yaml": yaml.NewEncoder(),
|
||||
"toml": toml.NewEncoder(),
|
||||
"xml": xml.NewEncoder(),
|
||||
"hcl": hcl.NewEncoder(),
|
||||
"yml": yaml.NewEncoder(),
|
||||
},
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func WithEncoder(e encoder.Encoder) Option {
|
||||
return func(o *Options) {
|
||||
if o.Encoding == nil {
|
||||
o.Encoding = make(map[string]encoder.Encoder)
|
||||
}
|
||||
o.Encoding[e.String()] = e
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisableReplaceEnvVars disables the environment variable interpolation preprocessor
|
||||
func WithDisableReplaceEnvVars() Option {
|
||||
return func(o *Options) {
|
||||
o.DisableReplaceEnvVars = true
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func ReplaceEnvVars(raw []byte) ([]byte, error) {
|
||||
re := regexp.MustCompile(`\$\{([A-Za-z0-9_]+)\}`)
|
||||
if re.Match(raw) {
|
||||
dataS := string(raw)
|
||||
res := re.ReplaceAllStringFunc(dataS, replaceEnvVars)
|
||||
return []byte(res), nil
|
||||
} else {
|
||||
return raw, nil
|
||||
}
|
||||
}
|
||||
|
||||
func replaceEnvVars(element string) string {
|
||||
v := element[2 : len(element)-1]
|
||||
el := os.Getenv(v)
|
||||
return el
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReplaceEnvVars(t *testing.T) {
|
||||
os.Setenv("myBar", "cat")
|
||||
os.Setenv("MYBAR", "cat")
|
||||
os.Setenv("my_Bar", "cat")
|
||||
os.Setenv("myBar_", "cat")
|
||||
|
||||
testData := []struct {
|
||||
expected string
|
||||
data []byte
|
||||
}{
|
||||
// Right use cases
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "cat"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${myBar}"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "cat"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${MYBAR}"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "cat"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${my_Bar}"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "cat"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${myBar_}"}}`),
|
||||
},
|
||||
// Wrong use cases
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "${myBar-}"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${myBar-}"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "${}"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${}"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "$sss}"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "$sss}"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "${sss"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "${sss"}}`),
|
||||
},
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "{something}"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "{something}"}}`),
|
||||
},
|
||||
// Use cases without replace env vars
|
||||
{
|
||||
`{"foo": "bar", "baz": {"bar": "cat"}}`,
|
||||
[]byte(`{"foo": "bar", "baz": {"bar": "cat"}}`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testData {
|
||||
res, err := ReplaceEnvVars(test.data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Compare(test.expected, string(res)) != 0 {
|
||||
t.Fatalf("Expected %s got %s", test.expected, res)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
// Package reader parses change sets and provides config values
|
||||
package reader
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/micro/go-micro/v3/config/source"
|
||||
)
|
||||
|
||||
// Reader is an interface for merging changesets
|
||||
type Reader interface {
|
||||
Merge(...*source.ChangeSet) (*source.ChangeSet, error)
|
||||
Values(*source.ChangeSet) (Values, error)
|
||||
String() string
|
||||
}
|
||||
|
||||
// Values is returned by the reader
|
||||
type Values interface {
|
||||
Bytes() []byte
|
||||
Get(path ...string) Value
|
||||
Set(val interface{}, path ...string)
|
||||
Del(path ...string)
|
||||
Map() map[string]interface{}
|
||||
Scan(v interface{}) error
|
||||
}
|
||||
|
||||
// Value represents a value of any type
|
||||
type Value interface {
|
||||
Bool(def bool) bool
|
||||
Int(def int) int
|
||||
String(def string) string
|
||||
Float64(def float64) float64
|
||||
Duration(def time.Duration) time.Duration
|
||||
StringSlice(def []string) []string
|
||||
StringMap(def map[string]string) map[string]string
|
||||
Scan(val interface{}) error
|
||||
Bytes() []byte
|
||||
}
|
70
config/secrets/encryption.go
Normal file
70
config/secrets/encryption.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// encrypt/decrypt functions are taken from https://www.melvinvivas.com/how-to-encrypt-and-decrypt-data-using-aes/
|
||||
|
||||
func encrypt(stringToEncrypt string, key []byte) (string, error) {
|
||||
plaintext := []byte(stringToEncrypt)
|
||||
|
||||
//Create a new Cipher Block from the key
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Create a new GCM - https://en.wikipedia.org/wiki/Galois/Counter_Mode
|
||||
//https://golang.org/pkg/crypto/cipher/#NewGCM
|
||||
aesGCM, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Create a nonce. Nonce should be from GCM
|
||||
nonce := make([]byte, aesGCM.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Encrypt the data using aesGCM.Seal
|
||||
//Since we don't want to save the nonce somewhere else in this case, we add it as a prefix to the encrypted data. The first nonce argument in Seal is the prefix.
|
||||
ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil)
|
||||
return fmt.Sprintf("%x", ciphertext), nil
|
||||
}
|
||||
|
||||
func decrypt(encryptedString string, key []byte) (string, error) {
|
||||
enc, _ := hex.DecodeString(encryptedString)
|
||||
|
||||
//Create a new Cipher Block from the key
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Create a new GCM
|
||||
aesGCM, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Get the nonce size
|
||||
nonceSize := aesGCM.NonceSize()
|
||||
|
||||
//Extract the nonce from the encrypted data
|
||||
nonce, ciphertext := enc[:nonceSize], enc[nonceSize:]
|
||||
|
||||
//Decrypt the data
|
||||
plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", plaintext), nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user