Skip to content

Commit 99060cf

Browse files
authored
feat: configure RPC UNIX socket attributes
2 parents c325b97 + 852af76 commit 99060cf

10 files changed

Lines changed: 167 additions & 16 deletions

File tree

config.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package rpc
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"net"
78
"strings"
89

@@ -12,7 +13,8 @@ import (
1213
// Config defines RPC service config.
1314
type Config struct {
1415
// Listen - address string (tcp://host:port or unix://file.sock).
15-
Listen string `mapstructure:"listen"`
16+
Listen string `mapstructure:"listen"`
17+
UnixSocket *tcplisten.UnixSocketOptions `mapstructure:"unix_socket"`
1618
}
1719

1820
// InitDefaults allows init blank config with a pre-defined set of default values.
@@ -41,12 +43,18 @@ func parseDSN(listen string) (dsn, error) {
4143
// Valid returns nil if config is valid.
4244
func (c *Config) Valid() error {
4345
_, err := parseDSN(c.Listen)
44-
return err
46+
if err != nil {
47+
return err
48+
}
49+
if err = c.UnixSocket.Validate(c.Listen); err != nil {
50+
return fmt.Errorf("rpc.unix_socket: %w", err)
51+
}
52+
return nil
4553
}
4654

4755
// Listener creates new rpc socket Listener.
4856
func (c *Config) Listener() (net.Listener, error) {
49-
return tcplisten.CreateListener(c.Listen)
57+
return tcplisten.CreateListenerWithOptions(c.Listen, c.UnixSocket)
5058
}
5159

5260
// Dialer creates rpc socket Dialer.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/roadrunner-server/endure/v2 v2.6.2
99
github.com/roadrunner-server/errors v1.5.0
1010
github.com/roadrunner-server/goridge/v4 v4.0.0-beta.3
11-
github.com/roadrunner-server/tcplisten v1.5.2
11+
github.com/roadrunner-server/tcplisten v1.6.0
1212
github.com/stretchr/testify v1.12.1
1313
)
1414

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ github.com/roadrunner-server/errors v1.5.0 h1:unG7LKIZrSzkCCF3YLRLA5VyqE0KKomofX
66
github.com/roadrunner-server/errors v1.5.0/go.mod h1:g9fo/T2C13cWRDR9PW1r0ZAOSQfNhWAZawyfkGiaHuI=
77
github.com/roadrunner-server/goridge/v4 v4.0.0-beta.3 h1:+kUw00/fpqwdMWrPMYW+OZH3O4gEar8hqrY7I+nAztA=
88
github.com/roadrunner-server/goridge/v4 v4.0.0-beta.3/go.mod h1:1aHppV68y/VqRED/AsfNg59sft9aQOhqgr5Z5n49jbM=
9-
github.com/roadrunner-server/tcplisten v1.5.2 h1:nn8yXYrhRDkfQ9AAu4V075uT4fZRmOnpxkawgE+bWPA=
10-
github.com/roadrunner-server/tcplisten v1.5.2/go.mod h1:DufGBz7Dlx2KrNe/4RukEvGMTqZKB0Uve1GztwcyyR8=
9+
github.com/roadrunner-server/tcplisten v1.6.0 h1:xfFeA2PZTmwJdwc/InhJGq200ew/lfTDReF3oa4AyI4=
10+
github.com/roadrunner-server/tcplisten v1.6.0/go.mod h1:M01BcmhsBiek8WfkiRQwVXwVamgZ5YV36Wa0hz937dA=
1111
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
1212
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
1313
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=

go.work.sum

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzA
8888
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
8989
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
9090
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
91+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9192
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE=
9293
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
9394
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
@@ -231,8 +232,6 @@ github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
231232
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
232233
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
233234
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
234-
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
235-
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
236235
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
237236
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
238237
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -243,6 +242,7 @@ github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Q
243242
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
244243
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
245244
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
245+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
246246
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
247247
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
248248
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
@@ -288,6 +288,7 @@ github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
288288
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
289289
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
290290
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
291+
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
291292
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
292293
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
293294
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
@@ -384,6 +385,7 @@ golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
384385
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
385386
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
386387
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
388+
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
387389
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
388390
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
389391
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
@@ -412,6 +414,7 @@ golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
412414
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
413415
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
414416
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
417+
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
415418
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
416419
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
417420
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -443,6 +446,7 @@ golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg
443446
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
444447
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
445448
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
449+
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
446450
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
447451
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
448452
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
@@ -499,10 +503,12 @@ google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7Qf
499503
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
500504
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
501505
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
506+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
502507
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
503508
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
504509
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
505510
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
511+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
506512
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
507513
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
508514
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=

schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
"examples": [
1515
"tcp://127.0.0.1:6001"
1616
]
17+
},
18+
"unix_socket": {
19+
"$ref": "https://raw.githubusercontent.com/roadrunner-server/tcplisten/v1.6.0/schema.json"
1720
}
1821
}
1922
}

tests/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ require (
2424
github.com/mattn/go-isatty v0.0.24 // indirect
2525
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
2626
github.com/roadrunner-server/errors v1.5.0 // indirect
27-
github.com/roadrunner-server/tcplisten v1.5.2 // indirect
27+
github.com/roadrunner-server/tcplisten v1.6.0 // indirect
2828
github.com/sagikazarmark/locafero v0.12.0 // indirect
2929
github.com/spf13/afero v1.15.0 // indirect
3030
github.com/spf13/cast v1.10.0 // indirect

tests/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ github.com/roadrunner-server/goridge/v4 v4.0.0-beta.3 h1:+kUw00/fpqwdMWrPMYW+OZH
3030
github.com/roadrunner-server/goridge/v4 v4.0.0-beta.3/go.mod h1:1aHppV68y/VqRED/AsfNg59sft9aQOhqgr5Z5n49jbM=
3131
github.com/roadrunner-server/logger/v6 v6.0.0-beta.4 h1:ukYPtYmFVfYUf2yzC3e81FaMA0r2qW/q2fhIGPNL+o0=
3232
github.com/roadrunner-server/logger/v6 v6.0.0-beta.4/go.mod h1:MZXC0lgYVGRSyRw4BWrvg3jQyj9pAMAW9hLINvC2x8E=
33-
github.com/roadrunner-server/tcplisten v1.5.2 h1:nn8yXYrhRDkfQ9AAu4V075uT4fZRmOnpxkawgE+bWPA=
34-
github.com/roadrunner-server/tcplisten v1.5.2/go.mod h1:DufGBz7Dlx2KrNe/4RukEvGMTqZKB0Uve1GztwcyyR8=
33+
github.com/roadrunner-server/tcplisten v1.6.0 h1:xfFeA2PZTmwJdwc/InhJGq200ew/lfTDReF3oa4AyI4=
34+
github.com/roadrunner-server/tcplisten v1.6.0/go.mod h1:M01BcmhsBiek8WfkiRQwVXwVamgZ5YV36Wa0hz937dA=
3535
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
3636
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
3737
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=

tests/helpers/rr.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const (
2828
// bootCfg holds the options applied to a container before it is started.
2929
type bootCfg struct {
3030
version string
31+
flags []string
3132
logLevel slog.Level
3233
probe func(ctx context.Context) bool
3334
}
@@ -40,6 +41,11 @@ func WithConfigVersion(v string) Option {
4041
return func(b *bootCfg) { b.version = v }
4142
}
4243

44+
// WithConfigFlags applies configuration overrides.
45+
func WithConfigFlags(flags ...string) Option {
46+
return func(b *bootCfg) { b.flags = flags }
47+
}
48+
4349
// WithLogLevel sets the endure container log level (debug by default).
4450
func WithLogLevel(l slog.Level) Option {
4551
return func(b *bootCfg) { b.logLevel = l }
@@ -132,10 +138,10 @@ func StartExpectNoListener(t *testing.T, cfgPath string, plugins []any, addr str
132138
}
133139

134140
// NewRPCClient dials the goridge rpc listener and closes the client on cleanup.
135-
func NewRPCClient(t *testing.T, address string) *rpc.Client {
141+
func NewRPCClient(t *testing.T, network, address string) *rpc.Client {
136142
t.Helper()
137143

138-
conn, err := new(net.Dialer).DialContext(t.Context(), "tcp", address)
144+
conn, err := new(net.Dialer).DialContext(t.Context(), network, address)
139145
require.NoError(t, err)
140146

141147
client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn))
@@ -156,7 +162,7 @@ func newContainer(t *testing.T, cfgPath string, plugins []any, opts []Option) (*
156162

157163
all := make([]any, 0, 2+len(plugins))
158164
all = append(all,
159-
&config.Plugin{Version: bc.version, Path: cfgPath},
165+
&config.Plugin{Version: bc.version, Path: cfgPath, Flags: bc.flags},
160166
&logger.Plugin{},
161167
)
162168

tests/rpc_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func rpcPlugins() []any {
2222
func TestServesRegisteredPlugin(t *testing.T) {
2323
helpers.Start(t, "configs/.rr.yaml", rpcPlugins(), helpers.WithTCPProbe(rpcAddr))
2424

25-
client := helpers.NewRPCClient(t, rpcAddr)
25+
client := helpers.NewRPCClient(t, "tcp", rpcAddr)
2626

2727
var got string
2828
require.NoError(t, client.Call("rpc_test.plugin1.Hello", "Valery", &got))
@@ -35,7 +35,7 @@ func TestServesRegisteredPlugin(t *testing.T) {
3535
func TestUnknownMethodIsRejected(t *testing.T) {
3636
helpers.Start(t, "configs/.rr.yaml", rpcPlugins(), helpers.WithTCPProbe(rpcAddr))
3737

38-
client := helpers.NewRPCClient(t, rpcAddr)
38+
client := helpers.NewRPCClient(t, "tcp", rpcAddr)
3939

4040
var got string
4141
err := client.Call("rpc_test.plugin1.NoSuchMethod", "Valery", &got)

tests/unix_socket_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//go:build linux || darwin || freebsd
2+
3+
package rpc
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"log/slog"
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
"time"
13+
14+
"tests/helpers"
15+
16+
"github.com/roadrunner-server/config/v6"
17+
"github.com/roadrunner-server/logger/v6"
18+
rpcPlugin "github.com/roadrunner-server/rpc/v6"
19+
"github.com/stretchr/testify/require"
20+
)
21+
22+
func TestUnixSocketInitErrors(t *testing.T) {
23+
cases := []struct {
24+
name string
25+
listen string
26+
options string
27+
wantErr string
28+
}{
29+
{name: "TCP options", listen: "tcp://127.0.0.1:0", options: `{mode: "0600"}`, wantErr: "filesystem unix:// address"},
30+
{name: "invalid mode", listen: "unix://rpc.sock", options: `{mode: "0780"}`, wantErr: "invalid unix socket mode"},
31+
{name: "unquoted mode", listen: "unix://rpc.sock", options: "{mode: 0600}", wantErr: "invalid unix socket mode"},
32+
{name: "empty socket address", listen: "unix://", options: `{mode: "0600"}`, wantErr: "filesystem unix:// address"},
33+
{name: "negative UID", listen: "unix://rpc.sock", options: "{uid: -1}", wantErr: "invalid unix socket uid"},
34+
{name: "negative GID", listen: "unix://rpc.sock", options: "{gid: -1}", wantErr: "invalid unix socket gid"},
35+
{name: "reserved UID", listen: "unix://rpc.sock", options: "{uid: 4294967295}", wantErr: "invalid unix socket uid"},
36+
{name: "reserved GID", listen: "unix://rpc.sock", options: "{gid: 4294967295}", wantErr: "invalid unix socket gid"},
37+
}
38+
39+
for _, tc := range cases {
40+
t.Run(tc.name, func(t *testing.T) {
41+
cfg := &config.Plugin{Path: unixSocketConfig(t, tc.listen, tc.options)}
42+
require.NoError(t, cfg.Init())
43+
log := logger.NewLogger(logger.ChannelConfig{}, slog.New(slog.DiscardHandler))
44+
p := &rpcPlugin.Plugin{}
45+
46+
require.ErrorContains(t, p.Init(cfg, log), tc.wantErr)
47+
})
48+
}
49+
}
50+
51+
func TestUnixSocketMode(t *testing.T) {
52+
cases := []struct {
53+
name string
54+
flags []string
55+
mode os.FileMode
56+
}{
57+
{name: "configured mode", mode: 0o600},
58+
{name: "mode override", flags: []string{"rpc.unix_socket.mode=0640"}, mode: 0o640},
59+
}
60+
61+
for _, tc := range cases {
62+
t.Run(tc.name, func(t *testing.T) {
63+
t.Chdir(t.TempDir())
64+
path := unixSocketConfig(t, "unix://rpc.sock", `{mode: "0600"}`)
65+
helpers.Start(t, path, rpcPlugins(), helpers.WithConfigFlags(tc.flags...))
66+
67+
info, err := os.Stat("rpc.sock")
68+
require.NoError(t, err)
69+
require.Equal(t, tc.mode, info.Mode().Perm())
70+
})
71+
}
72+
}
73+
74+
func TestServesUnixSocket(t *testing.T) {
75+
t.Chdir(t.TempDir())
76+
path := unixSocketConfig(t, "unix://rpc.sock", `{mode: "0600"}`)
77+
helpers.Start(t, path, rpcPlugins())
78+
client := helpers.NewRPCClient(t, "unix", "rpc.sock")
79+
80+
var got string
81+
require.NoError(t, client.Call("rpc_test.plugin1.Hello", "Valery", &got))
82+
require.Equal(t, "Hello, username: Valery", got)
83+
}
84+
85+
func TestUnixSocketStopRemovesListener(t *testing.T) {
86+
t.Chdir(t.TempDir())
87+
path := unixSocketConfig(t, "unix://rpc.sock", `{mode: "0600"}`)
88+
stop := helpers.Start(t, path, rpcPlugins())
89+
require.FileExists(t, "rpc.sock")
90+
91+
stop()
92+
93+
require.NoFileExists(t, "rpc.sock")
94+
}
95+
96+
func TestUnixSocketOwnershipErrorRemovesListener(t *testing.T) {
97+
if os.Geteuid() == 0 {
98+
t.Skip("Requires an unprivileged process.")
99+
}
100+
t.Chdir(t.TempDir())
101+
cfg := &config.Plugin{Path: unixSocketConfig(t, "unix://rpc.sock", "{uid: 0}")}
102+
require.NoError(t, cfg.Init())
103+
log := logger.NewLogger(logger.ChannelConfig{}, slog.New(slog.DiscardHandler))
104+
p := &rpcPlugin.Plugin{}
105+
require.NoError(t, p.Init(cfg, log))
106+
t.Cleanup(func() { require.NoError(t, p.Stop(context.Background())) })
107+
108+
select {
109+
case err := <-p.Serve():
110+
require.ErrorContains(t, err, "chown unix socket")
111+
case <-time.After(5 * time.Second):
112+
t.Fatal("expected an ownership error")
113+
}
114+
require.NoFileExists(t, "rpc.sock")
115+
}
116+
117+
func unixSocketConfig(t *testing.T, listen, options string) string {
118+
t.Helper()
119+
120+
contents := fmt.Sprintf(`version: "3"
121+
rpc:
122+
listen: %q
123+
unix_socket: %s
124+
`, listen, options)
125+
path := filepath.Join(t.TempDir(), ".rr.yaml")
126+
require.NoError(t, os.WriteFile(path, []byte(contents), 0o600))
127+
return path
128+
}

0 commit comments

Comments
 (0)