From 298dbb285801c18a496c46eb622525c09d93ca3a Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Fri, 11 Oct 2024 10:44:40 +0200 Subject: [PATCH 1/2] Allow to define the IP on which `server:start` should listen to In addition to the flags, one can also uses the SYMFONY_LISTEN_IP and SYMFONY_ALL_IP environment variables --- Dockerfile | 2 ++ commands/local_server_start.go | 2 ++ local/http/http.go | 3 ++- local/process/listener.go | 14 ++++++++++---- local/project/config.go | 6 ++++++ local/project/project.go | 1 + 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 156db0cd..1ae27216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ COPY symfony /usr/local/bin/ FROM scratch +ENV SYMFONY_ALLOW_ALL_IP=true + ENTRYPOINT ["/usr/local/bin/symfony"] COPY --from=build . . diff --git a/commands/local_server_start.go b/commands/local_server_start.go index 138fb66d..9df40292 100644 --- a/commands/local_server_start.go +++ b/commands/local_server_start.go @@ -65,6 +65,8 @@ var localServerStartCmd = &console.Command{ &console.StringFlag{Name: "document-root", Usage: "Project document root (auto-configured by default)"}, &console.StringFlag{Name: "passthru", Usage: "Project passthru index (auto-configured by default)"}, &console.IntFlag{Name: "port", DefaultValue: 8000, Usage: "Preferred HTTP port"}, + &console.StringFlag{Name: "listen-ip", DefaultValue: "127.0.0.1", Usage: "The IP on which the CLI should listen"}, + &console.BoolFlag{Name: "allow-all-ip", Usage: "Listen on all the available interfaces"}, &console.BoolFlag{Name: "daemon", Aliases: []string{"d"}, Usage: "Run the server in the background"}, &console.BoolFlag{Name: "no-humanize", Usage: "Do not format JSON logs"}, &console.StringFlag{Name: "p12", Usage: "Name of the file containing the TLS certificate to use in p12 format"}, diff --git a/local/http/http.go b/local/http/http.go index ddac3163..859dd25b 100644 --- a/local/http/http.go +++ b/local/http/http.go @@ -49,6 +49,7 @@ type Server struct { Callback ServerCallback Port int PreferredPort int + ListenIp string PKCS12 string AllowHTTP bool Logger zerolog.Logger @@ -79,7 +80,7 @@ var gzipContentTypes = []string{ // Start starts the server func (s *Server) Start(errChan chan error) (int, error) { - ln, port, err := process.CreateListener(s.Port, s.PreferredPort) + ln, port, err := process.CreateListener(s.ListenIp, s.Port, s.PreferredPort) if err != nil { return port, errors.WithStack(err) } diff --git a/local/process/listener.go b/local/process/listener.go index 02ec7b81..a5b21368 100644 --- a/local/process/listener.go +++ b/local/process/listener.go @@ -20,8 +20,8 @@ package process import ( + "fmt" "net" - "strconv" "github.com/pkg/errors" ) @@ -29,7 +29,7 @@ import ( // CreateListener creates a listener on a port // Pass a preferred port (will increment by 1 if port is not available) // or pass 0 to auto-find any available port -func CreateListener(port, preferredPort int) (net.Listener, int, error) { +func CreateListener(listenIp string, port, preferredPort int) (net.Listener, int, error) { var ln net.Listener var err error tryPort := preferredPort @@ -39,9 +39,15 @@ func CreateListener(port, preferredPort int) (net.Listener, int, error) { max = 1 } for { - ln, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(tryPort)) + // we really want to test availability on 127.0.0.1 + ln, err = net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", tryPort)) if err == nil { - break + ln.Close() + // but then, we want to listen to as many local IP's as possible + ln, err = net.Listen("tcp", fmt.Sprintf("%s:%d", listenIp, tryPort)) + if err == nil { + break + } } if port > 0 { return nil, 0, errors.Wrapf(err, "unable to listen on port %d", port) diff --git a/local/project/config.go b/local/project/config.go index 0ea217bf..307f01bf 100644 --- a/local/project/config.go +++ b/local/project/config.go @@ -35,6 +35,7 @@ const DockerComposeWorkerKey = "docker_compose" type Config struct { HomeDir string ProjectDir string + ListenIp string DocumentRoot string `yaml:"document_root"` Passthru string `yaml:"passthru"` Port int `yaml:"port"` @@ -83,6 +84,11 @@ func NewConfigFromContext(c *console.Context, projectDir string) (*Config, *File } config.AppVersion = c.App.Version config.ProjectDir = projectDir + if c.IsSet("allow-all-ip") { + config.ListenIp = "" + } else { + config.ListenIp = c.String("listen-ip") + } if c.IsSet("document-root") { config.DocumentRoot = c.String("document-root") } diff --git a/local/project/project.go b/local/project/project.go index a9605c65..a4fbbc88 100644 --- a/local/project/project.go +++ b/local/project/project.go @@ -56,6 +56,7 @@ func New(c *Config) (*Project, error) { DocumentRoot: documentRoot, Port: c.Port, PreferredPort: c.PreferredPort, + ListenIp: c.ListenIp, Logger: c.Logger, PKCS12: c.PKCS12, AllowHTTP: c.AllowHTTP, From 903639a40a1cd274414f9d78784236e6d94c2230 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Mon, 9 Sep 2024 15:12:38 +0200 Subject: [PATCH 2/2] refacto: improve Docker Compose project name determination By using the library constants and reading the yaml files --- envs/docker.go | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/envs/docker.go b/envs/docker.go index c0c195cf..eb97e8f4 100644 --- a/envs/docker.go +++ b/envs/docker.go @@ -32,10 +32,13 @@ import ( "strings" "time" + compose "github.com/compose-spec/compose-go/cli" + composeConsts "github.com/compose-spec/compose-go/consts" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" docker "github.com/docker/docker/client" "github.com/symfony-cli/terminal" + "gopkg.in/yaml.v2" ) var ( @@ -491,7 +494,7 @@ func getEnvValue(env string, key string) string { func (l *Local) getComposeProjectName() string { // https://docs.docker.com/compose/reference/envvars/#compose_project_name - if project := os.Getenv("COMPOSE_PROJECT_NAME"); project != "" { + if project := os.Getenv(composeConsts.ComposeProjectName); project != "" { return project } @@ -507,13 +510,36 @@ func (l *Local) getComposeProjectName() string { if _, err := os.Stat(filepath.Join(composeDir, ".env")); err == nil { if contents, err := os.ReadFile(filepath.Join(composeDir, ".env")); err == nil { for _, line := range bytes.Split(contents, []byte("\n")) { - if bytes.HasPrefix(line, []byte("COMPOSE_PROJECT_NAME=")) { - return string(line[len("COMPOSE_PROJECT_NAME="):]) + if bytes.HasPrefix(line, []byte(composeConsts.ComposeProjectName+"=")) { + return string(line[len(composeConsts.ComposeProjectName)+1:]) } } } } + // Compose project name can be set in every Docker Compose file + for index, filename := range compose.DefaultFileNames { + if _, err := os.Stat(filepath.Join(composeDir, filename)); err != nil { + continue + } + + for _, filename := range []string{compose.DefaultOverrideFileNames[index], filename} { + buf, err := os.ReadFile(filepath.Join(composeDir, filename)) + if err != nil { + continue + } + + config := struct { + ProjectName string `yaml:"name"` + }{} + + // unmarshall the content of the file to get the project name + if err := yaml.Unmarshal(buf, &config); err == nil && config.ProjectName != "" { + return config.ProjectName + } + } + } + return filepath.Base(composeDir) } @@ -526,7 +552,7 @@ func (l *Local) getComposeDir() string { // look for the first dir up with a docker-composer.ya?ml file (in case of a multi-project) dir := l.Dir for { - for _, filename := range []string{"compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"} { + for _, filename := range compose.DefaultFileNames { if _, err := os.Stat(filepath.Join(dir, filename)); err == nil { return dir }