mirror of
https://github.com/hanzoai/psrpc.git
synced 2026-09-30 23:47:22 +00:00
generate the service half, and reach a sibling container at the host
Two failures, neither in this code.
internal/test/my_service could not build: `undefined: MyServiceServer`. Its
.psrpc.go half is gitignored on purpose (internal/test/.gitignore carries
**/*.psrpc.go) — generated, not committed — so a fresh checkout has the
.pb.go and none of the service code that goes with it, and nothing in the
gate ever generated it. It does now, from the plugin in this repo. Only the
psrpc half: the .pb.go beside it IS committed, and regenerating that too
rewrites it against whatever protoc the runner carries.
From the package directory, as gen.go does. paths=source_relative places
output beside the .proto AS NAMED, so running it from the root writes
my_service.psrpc.go into the root and every build then reports `found
packages psrpc and my_service in <root>`.
internal/bus dialled 127.0.0.1 for containers it had just started:
bustest.go:51: dial tcp 127.0.0.1:32769: connect: connection refused
CI runs the job inside a container with the host's /var/run/docker.sock
bound in, so nats and redis come up as SIBLINGS — published on the HOST's
loopback, while the job's own 127.0.0.1 is a different interface with
nothing on it. dockertest reports the daemon's view; the port it chose is
right and the host is not, so hostPort keeps the port and answers the host:
the bridge gateway from inside a container, loopback outside one, and
DOCKER_TEST_HOST ahead of both.
Verified against a real daemon: internal/bus goes from refused to ok in 18s,
and the gate's own command — generate, build, then the package list it already
uses — passes 19 packages, exit 0.
This commit is contained in:
@@ -6,6 +6,27 @@
|
||||
# nats and redis containers through dockertest, so it is red or green according
|
||||
# to the runner's docker daemon rather than this code. Excluded by list, not by
|
||||
# a flag, so what is skipped is visible in the command itself.
|
||||
#
|
||||
# internal/test/my_service needs a generate step first. Its .psrpc.go half is
|
||||
# gitignored on purpose (internal/test/.gitignore: **/*.psrpc.go) — generated,
|
||||
# not committed — so a fresh checkout has the .pb.go and not the service code
|
||||
# that goes with it, and the package cannot build: `undefined: MyServiceServer`.
|
||||
# Only the psrpc half is generated here; the .pb.go beside it IS committed, and
|
||||
# regenerating that too would rewrite it against whatever protoc the runner
|
||||
# happens to carry.
|
||||
test:
|
||||
- name: test
|
||||
run: go build ./... && go test $(go list ./... | grep -v '/internal/test$')
|
||||
run: |
|
||||
set -e
|
||||
command -v protoc >/dev/null || { sudo apt-get update && sudo apt-get install -y protobuf-compiler; }
|
||||
BIN=$(mktemp -d)
|
||||
go build -o "$BIN/protoc-gen-psrpc" ./protoc-gen-psrpc
|
||||
# From the package directory, as gen.go does: paths=source_relative
|
||||
# places the output beside the .proto as it was NAMED, so invoking this
|
||||
# from the repo root writes my_service.psrpc.go into the root and gives
|
||||
# `found packages psrpc and my_service in <root>`.
|
||||
( cd internal/test/my_service \
|
||||
&& PATH="$BIN:$PATH" protoc \
|
||||
--psrpc_out=paths=source_relative:. \
|
||||
-I ../../../protoc-gen-psrpc/options -I=. my_service.proto )
|
||||
go build ./... && go test $(go list ./... | grep -v '/internal/test$')
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package bustest
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// hostPort is where a test reaches a container it just started.
|
||||
//
|
||||
// dockertest reports the published address from the daemon's point of view, and
|
||||
// that is this machine only when the test and the daemon share one. In CI they
|
||||
// do not: the job runs in a container with the host's /var/run/docker.sock bound
|
||||
// in, so a container it starts is a SIBLING — published on the HOST's loopback,
|
||||
// while the job's own 127.0.0.1 is a different interface with nothing on it. The
|
||||
// server comes up, every dial is refused, and the retry loop blames the server:
|
||||
//
|
||||
// bustest.go:51: dial tcp 127.0.0.1:32769: connect: connection refused
|
||||
//
|
||||
// The port dockertest chose is right, so keep it and answer the host ourselves.
|
||||
func hostPort(addr string) string {
|
||||
_, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return addr
|
||||
}
|
||||
return net.JoinHostPort(dockerHost(), port)
|
||||
}
|
||||
|
||||
// dockerHost is loopback outside a container, and the host as seen from inside
|
||||
// one. DOCKER_TEST_HOST overrides both, for a daemon that is somewhere else.
|
||||
func dockerHost() string {
|
||||
if h := os.Getenv("DOCKER_TEST_HOST"); h != "" {
|
||||
return h
|
||||
}
|
||||
|
||||
// /.dockerenv exists only inside a container.
|
||||
if _, err := os.Stat("/.dockerenv"); err != nil {
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
// host.docker.internal is the documented name, but only Docker Desktop
|
||||
// publishes it; on a plain Linux daemon it does not resolve.
|
||||
if addrs, err := net.LookupHost("host.docker.internal"); err == nil && len(addrs) > 0 {
|
||||
return addrs[0]
|
||||
}
|
||||
|
||||
// The default route is the bridge gateway, which IS the host from in here.
|
||||
if gw := defaultGateway(); gw != "" {
|
||||
return gw
|
||||
}
|
||||
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
// defaultGateway reads the container's default route out of /proc. The kernel
|
||||
// writes the address little-endian in hex, so 0100007F is 127.0.0.1.
|
||||
func defaultGateway() string {
|
||||
f, err := os.Open("/proc/net/route")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer f.Close() //nolint:errcheck // read-only
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
fields := strings.Fields(s.Text())
|
||||
if len(fields) < 3 || fields[1] != "00000000" { // destination 0.0.0.0
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := strconv.ParseUint(fields[2], 16, 32)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return net.IPv4(byte(v), byte(v>>8), byte(v>>16), byte(v>>24)).String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -31,7 +31,7 @@ func NewKV(t testing.TB, pool dockertest.Pool) Server {
|
||||
t.Cleanup(func() {
|
||||
_ = c.Close(context.Background())
|
||||
})
|
||||
addr := c.GetHostPort("6379/tcp")
|
||||
addr := hostPort(c.GetHostPort("6379/tcp"))
|
||||
waitTCPPort(t, pool, addr)
|
||||
|
||||
t.Log("KV running on", addr)
|
||||
|
||||
@@ -30,7 +30,7 @@ func NewNATS(t testing.TB, pool dockertest.Pool) Server {
|
||||
t.Cleanup(func() {
|
||||
_ = c.Close(context.Background())
|
||||
})
|
||||
addr := c.GetHostPort("4222/tcp")
|
||||
addr := hostPort(c.GetHostPort("4222/tcp"))
|
||||
waitTCPPort(t, pool, addr)
|
||||
|
||||
t.Log("NATS running on", addr)
|
||||
|
||||
Reference in New Issue
Block a user