Skip to content

Commit 68edd30

Browse files
authored
[services] Let users set the port for process-compose (#2299)
## Summary Users can set the port that process-compose runs on when they use `devbox services`. If they do not specify a port, we default to randomly picking an open port with low risk of conflict Users can specify their process-compose port by: 1. Passing the `--pcport, -p` flag when running `devbox services up`. 2. Setting the `PC_PORT_NUM` environment variable. This variable can be set in the projects `devbox.json` if you want to always use the same port for process-compose. This is overridden by the `--pcport` flag. ## How was it tested? In the MySQL example: 1. When I run `devbox services up -b -p 8080`, or 2. When I run `PC_PORT_NUM devbox services up -b`, I can verify that process-compose is running on my 8080 with `curl localhost:8080/processes` and seeing the list of expected processes.
1 parent 3818683 commit 68edd30

File tree

5 files changed

+60
-21
lines changed

5 files changed

+60
-21
lines changed

internal/boxcli/services.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type serviceUpFlags struct {
2020
background bool
2121
processComposeFile string
2222
processComposeFlags []string
23+
pcport int
2324
}
2425

2526
type serviceStopFlags struct {
@@ -38,6 +39,8 @@ func (flags *serviceUpFlags) register(cmd *cobra.Command) {
3839
&flags.background, "background", "b", false, "run service in background")
3940
cmd.Flags().StringArrayVar(
4041
&flags.processComposeFlags, "pcflags", []string{}, "pass flags directly to process compose")
42+
cmd.Flags().IntVarP(
43+
&flags.pcport, "pcport", "p", 0, "specify the port for process-compose to use. You can also set the pcport by exporting DEVBOX_PC_PORT_NUM")
4144
}
4245

4346
func (flags *serviceStopFlags) register(cmd *cobra.Command) {
@@ -245,6 +248,10 @@ func startProcessManager(
245248
return err
246249
}
247250

251+
if flags.pcport < 0 {
252+
return errors.Errorf("invalid pcport %d: ports cannot be less than 0", flags.pcport)
253+
}
254+
248255
box, err := devbox.Open(&devopt.Opts{
249256
Dir: servicesFlags.config.path,
250257
Env: env,
@@ -261,8 +268,9 @@ func startProcessManager(
261268
servicesFlags.runInCurrentShell,
262269
args,
263270
devopt.ProcessComposeOpts{
264-
Background: flags.background,
265-
ExtraFlags: flags.processComposeFlags,
271+
Background: flags.background,
272+
ExtraFlags: flags.processComposeFlags,
273+
ProcessComposePort: flags.pcport,
266274
},
267275
)
268276
}

internal/devbox/devopt/devboxopts.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ type Opts struct {
1818
}
1919

2020
type ProcessComposeOpts struct {
21-
ExtraFlags []string
22-
Background bool
21+
ExtraFlags []string
22+
Background bool
23+
ProcessComposePort int
2324
}
2425

2526
type GenerateOpts struct {

internal/devbox/services.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package devbox
33
import (
44
"context"
55
"fmt"
6+
"strconv"
67
"text/tabwriter"
78

89
"go.jetpack.io/devbox/internal/boxcli/usererr"
@@ -217,6 +218,9 @@ func (d *Devbox) StartProcessManager(
217218
for _, flag := range processComposeOpts.ExtraFlags {
218219
args = append(args, "--pcflags", flag)
219220
}
221+
if processComposeOpts.ProcessComposePort != 0 {
222+
args = append(args, "--pcport", strconv.Itoa(processComposeOpts.ProcessComposePort))
223+
}
220224

221225
return d.runDevboxServicesScript(ctx, args)
222226
}
@@ -254,9 +258,10 @@ func (d *Devbox) StartProcessManager(
254258
svcs,
255259
d.projectDir,
256260
services.ProcessComposeOpts{
257-
BinPath: processComposeBinPath,
258-
Background: processComposeOpts.Background,
259-
ExtraFlags: processComposeOpts.ExtraFlags,
261+
BinPath: processComposeBinPath,
262+
Background: processComposeOpts.Background,
263+
ExtraFlags: processComposeOpts.ExtraFlags,
264+
ProcessComposePort: processComposeOpts.ProcessComposePort,
260265
},
261266
)
262267
}

internal/services/manager.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ type globalProcessComposeConfig struct {
4141
}
4242

4343
type ProcessComposeOpts struct {
44-
BinPath string
45-
ExtraFlags []string
46-
Background bool
44+
BinPath string
45+
ExtraFlags []string
46+
Background bool
47+
ProcessComposePort int
4748
}
4849

4950
func newGlobalProcessComposeConfig() *globalProcessComposeConfig {
@@ -128,10 +129,9 @@ func StartProcessManager(
128129
config := readGlobalProcessComposeJSON(configFile)
129130
config.File = configFile
130131

131-
// Get the port to use for this project
132-
port, err := getAvailablePort()
132+
port, err := selectPort(processComposeConfig.ProcessComposePort)
133133
if err != nil {
134-
return err
134+
return fmt.Errorf("failed to select port: %v", err)
135135
}
136136

137137
// Start building the process-compose command

internal/services/ports.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package services
22

33
import (
4+
"fmt"
45
"net"
6+
"os"
7+
"strconv"
58

69
"github.com/pkg/errors"
710
)
@@ -33,17 +36,11 @@ var disallowedPorts = map[int]string{
3336

3437
func getAvailablePort() (int, error) {
3538
get := func() (int, error) {
36-
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
39+
port, err := isPortAvailable(0)
3740
if err != nil {
3841
return 0, errors.WithStack(err)
3942
}
40-
41-
l, err := net.ListenTCP("tcp", addr)
42-
if err != nil {
43-
return 0, errors.WithStack(err)
44-
}
45-
defer l.Close()
46-
return l.Addr().(*net.TCPAddr).Port, nil
43+
return port, nil
4744
}
4845

4946
for range 1000 {
@@ -60,6 +57,34 @@ func getAvailablePort() (int, error) {
6057
return 0, errors.New("no available port")
6158
}
6259

60+
func selectPort(configPort int) (int, error) {
61+
if configPort != 0 {
62+
return isPortAvailable(configPort)
63+
}
64+
65+
if portStr, exists := os.LookupEnv("DEVBOX_PC_PORT_NUM"); exists {
66+
port, err := strconv.Atoi(portStr)
67+
if err != nil {
68+
return 0, fmt.Errorf("invalid DEVBOX_PC_PORT_NUM environment variable: %v", err)
69+
}
70+
if port <= 0 {
71+
return 0, fmt.Errorf("invalid DEVBOX_PC_PORT_NUM environment variable: ports cannot be less than 0")
72+
}
73+
return isPortAvailable(port)
74+
}
75+
76+
return getAvailablePort()
77+
}
78+
6379
func isAllowed(port int) bool {
6480
return port > 1024 && disallowedPorts[port] == ""
6581
}
82+
83+
func isPortAvailable(port int) (int, error) {
84+
ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
85+
if err != nil {
86+
return 0, fmt.Errorf("port %d is already in use", port)
87+
}
88+
defer ln.Close()
89+
return ln.Addr().(*net.TCPAddr).Port, nil
90+
}

0 commit comments

Comments
 (0)