Skip to content

Commit 5da2f58

Browse files
authored
Support port flag overrides (#44)
1 parent 531c355 commit 5da2f58

7 files changed

Lines changed: 71 additions & 19 deletions

File tree

.bazelversion

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9.0.0rc5
1+
9.0.0

.bcr/presubmit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ bcr_test_module:
22
module_path: "tests"
33
matrix:
44
platform: ["debian10", "macos", "ubuntu2004", "windows"]
5-
bazel: [7.x, 8.x, rolling]
5+
bazel: [7.x, 8.x, 9.x, rolling]
66
tasks:
77
run_tests:
88
name: "Run tests"

cmd/svcinit/main.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import (
88
"errors"
99
"fmt"
1010
"log"
11+
"maps"
1112
"math"
1213
"net"
1314
"os"
1415
"os/exec"
1516
"os/signal"
16-
"slices"
1717
"strconv"
1818
"strings"
1919
"syscall"
@@ -360,15 +360,15 @@ func assignPorts(
360360
ports := svclib.Ports{}
361361

362362
for label, spec := range serviceSpecs {
363-
namedPorts := slices.Clone(spec.NamedPorts)
363+
namedPorts := maps.Clone(spec.NamedPorts)
364364
if spec.AutoassignPort {
365-
namedPorts = append(namedPorts, "")
365+
namedPorts[""] = spec.Port
366366
}
367367

368368
// Note, this can cause collisions. So be careful!
369369
// To avoid port collisions, set the `so_reuseport_aware` option on the service definition
370370
// and use the SO_REUSEPORT socket option in your services.
371-
for _, portName := range namedPorts {
371+
for portName, port := range namedPorts {
372372
// We do a bit of a dance here to set SO_LINGER to 0. For details, see
373373
// https://stackoverflow.com/questions/71975992/what-really-is-the-linger-time-that-can-be-set-with-so-linger-on-sockets
374374
lc := net.ListenConfig{
@@ -387,18 +387,18 @@ func assignPorts(
387387
},
388388
}
389389

390-
listener, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:0")
390+
listener, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:"+port)
391391
if err != nil {
392392
return nil, err
393393
}
394-
_, port, err := net.SplitHostPort(listener.Addr().String())
394+
_, port, err = net.SplitHostPort(listener.Addr().String())
395395
if err != nil {
396396
return nil, err
397397
}
398398

399399
qualifiedPortName := label
400400
if portName != "" {
401-
qualifiedPortName += ":" + portName
401+
qualifiedPortName += "." + portName
402402
}
403403

404404
if !terseOutput {
@@ -407,6 +407,20 @@ func assignPorts(
407407

408408
ports.Set(qualifiedPortName, port)
409409

410+
{
411+
// TODO(zbarsky): Clean this up after April 2026
412+
qualifiedPortName := label
413+
if portName != "" {
414+
qualifiedPortName += ":" + portName
415+
}
416+
417+
if !terseOutput {
418+
log.Printf("Assigning port %s to %s\n", port, qualifiedPortName)
419+
}
420+
421+
ports.Set(qualifiedPortName, port)
422+
}
423+
410424
if !spec.SoReuseportAware {
411425
toClose = append(toClose, listener)
412426
}
@@ -424,10 +438,20 @@ func assignPorts(
424438
for portName, aliasedTo := range spec.PortAliases {
425439
qualifiedPortName := label
426440
if portName != "" {
427-
qualifiedPortName += ":" + portName
441+
qualifiedPortName += "." + portName
428442
}
429443

430444
ports.Set(qualifiedPortName, ports[aliasedTo])
445+
446+
{
447+
// TODO(zbarsky): Clean this up after April 2026
448+
qualifiedPortName := label
449+
if portName != "" {
450+
qualifiedPortName += ":" + portName
451+
}
452+
453+
ports.Set(qualifiedPortName, ports[aliasedTo])
454+
}
431455
}
432456
}
433457

@@ -516,6 +540,7 @@ func augmentServiceSpecs(
516540
Replacement{Old: "$${TMPDIR}", New: tmpDir},
517541
Replacement{Old: "$${SOCKET_DIR}", New: socketDir},
518542
)
543+
519544
for label, port := range ports {
520545
replacements = append(replacements, Replacement{
521546
Old: "$${" + label + "}",

docs/itest.md

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

itest.bzl

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Rules for running services in integration tests. """
22

3+
load("@bazel_skylib//rules:common_settings.bzl", "int_flag")
34
load(
45
"//private:itest.bzl",
56
_itest_service = "itest_service",
@@ -12,12 +13,32 @@ def port(label):
1213
return "$${%s}" % (str(native.package_relative_label(label)))
1314

1415
def named_port(label, name):
15-
return "$${%s:%s}" % (str(native.package_relative_label(label)), name)
16+
return port(label + "." + name)
17+
18+
def itest_service(name, tags = [], hygienic = True, named_ports = [], **kwargs):
19+
if "port" in kwargs:
20+
fail("Do not specify `port`, instead set it via the `%s` flag" % (name + ".port"))
21+
22+
int_flag(
23+
name = name + ".port",
24+
build_setting_default = 0,
25+
)
26+
27+
named_ports_attr = {}
28+
for named_port in named_ports:
29+
named_port_label = name + "." + named_port
30+
named_ports_attr[named_port_label] = named_port
31+
32+
int_flag(
33+
name = named_port_label,
34+
build_setting_default = 0,
35+
)
1636

17-
def itest_service(name, tags = [], hygienic = True, **kwargs):
1837
_itest_service(
1938
name = name,
2039
tags = tags + ["ibazel_notify_changes"],
40+
port = name + ".port",
41+
named_ports = named_ports_attr,
2142
**kwargs
2243
)
2344

private/itest.bzl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,14 @@ def _itest_service_impl(ctx):
204204
extra_service_spec_kwargs = {
205205
"type": "service",
206206
"http_health_check_address": ctx.attr.http_health_check_address,
207+
"port": str(ctx.attr.port[BuildSettingInfo].value),
207208
"autoassign_port": ctx.attr.autoassign_port,
208209
"so_reuseport_aware": ctx.attr.so_reuseport_aware,
209210
"deferred": ctx.attr.deferred,
210-
"named_ports": ctx.attr.named_ports,
211+
"named_ports": {
212+
name: str(port_flag[BuildSettingInfo].value)
213+
for port_flag, name in ctx.attr.named_ports.items()
214+
},
211215
"hot_reloadable": ctx.attr.hot_reloadable,
212216
"expected_start_duration": ctx.attr.expected_start_duration,
213217
"health_check_interval": ctx.attr.health_check_interval,
@@ -249,10 +253,11 @@ _itest_service_attrs = _itest_binary_attrs | {
249253
For example, the following Bash command:
250254
`PORT=$($GET_ASSIGNED_PORT_BIN @@//label/for:service)`""",
251255
),
252-
"named_ports": attr.string_list(
256+
"port": attr.label(doc="Internal"),
257+
"named_ports": attr.label_keyed_string_dict(
253258
doc = """For each element of the list, the service manager will pick a free port and assign it to the service.
254259
The port's fully-qualified name is the service's fully-qualified label and the port name, separated by a colon.
255-
For example, a port assigned with `named_ports = ["http_port"]` will be assigned a fully-qualified name of `@@//label/for:service:http_port`.
260+
For example, a port assigned with `named_ports = ["http_port"]` will be assigned a fully-qualified name of `@@//label/for:service.http_port`.
256261
257262
Named ports are accessible through the service-port mapping. For more details, see `autoassign_port`.""",
258263
),

svclib/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ type ServiceSpec struct {
1919
HealthCheckTimeout string `json:"health_check_timeout"`
2020
VersionFile string `json:"version_file"`
2121
Deps []string `json:"deps"`
22+
Port string `json:"port"`
2223
AutoassignPort bool `json:"autoassign_port"`
2324
SoReuseportAware bool `json:"so_reuseport_aware"`
24-
NamedPorts []string `json:"named_ports"`
25+
NamedPorts map[string]string `json:"named_ports"`
2526
HotReloadable bool `json:"hot_reloadable"`
2627
PortAliases map[string]string `json:"port_aliases"`
2728
ShutdownSignal string `json:"shutdown_signal"`

0 commit comments

Comments
 (0)