"fmt"
"io/ioutil"
"net"
+ "net/url"
"os"
"os/exec"
+ "os/user"
"path/filepath"
"regexp"
"SSLKEY": filepath.Join(super.tempdir, "server.key"),
"ACCESSLOG": filepath.Join(super.tempdir, "nginx_access.log"),
"ERRORLOG": filepath.Join(super.tempdir, "nginx_error.log"),
- "TMPDIR": super.tempdir,
+ "TMPDIR": super.wwwtempdir,
}
for _, cmpt := range []struct {
varname string
{"WORKBENCH1", super.cluster.Services.Workbench1},
{"WS", super.cluster.Services.Websocket},
} {
- port, err := internalPort(cmpt.svc)
- if err != nil {
+ var host, port string
+ if len(cmpt.svc.InternalURLs) == 0 {
+ // We won't run this service, but we need an
+ // upstream port to write in our templated
+ // nginx config. Choose a port that will
+ // return 502 Bad Gateway.
+ port = "9"
+ } else if host, port, err = internalPort(cmpt.svc); err != nil {
return fmt.Errorf("%s internal port: %w (%v)", cmpt.varname, err, cmpt.svc)
- }
- if ok, err := addrIsLocal(net.JoinHostPort(super.ListenHost, port)); !ok || err != nil {
- return fmt.Errorf("urlIsLocal() failed for host %q port %q: %v", super.ListenHost, port, err)
+ } else if ok, err := addrIsLocal(net.JoinHostPort(host, port)); !ok || err != nil {
+ return fmt.Errorf("%s addrIsLocal() failed for host %q port %q: %v", cmpt.varname, host, port, err)
}
vars[cmpt.varname+"PORT"] = port
if err != nil {
return fmt.Errorf("%s external port: %w (%v)", cmpt.varname, err, cmpt.svc)
}
- if ok, err := addrIsLocal(net.JoinHostPort(super.ListenHost, port)); !ok || err != nil {
- return fmt.Errorf("urlIsLocal() failed for host %q port %q: %v", super.ListenHost, port, err)
+ listenAddr := net.JoinHostPort(super.ListenHost, port)
+ if ok, err := addrIsLocal(listenAddr); !ok || err != nil {
+ return fmt.Errorf("%s addrIsLocal(%q) failed: %w", cmpt.varname, listenAddr, err)
}
vars[cmpt.varname+"SSLPORT"] = port
}
}
}
}
+
+ args := []string{
+ "-g", "error_log stderr info;",
+ "-g", "pid " + filepath.Join(super.wwwtempdir, "nginx.pid") + ";",
+ "-c", conffile,
+ }
+ // Nginx ignores "user www-data;" when running as a non-root
+ // user... except that it causes it to ignore our other -g
+ // options. So we still have to decide for ourselves whether
+ // it's needed.
+ if u, err := user.Current(); err != nil {
+ return fmt.Errorf("user.Current(): %w", err)
+ } else if u.Uid == "0" {
+ args = append([]string{"-g", "user www-data;"}, args...)
+ }
+
super.waitShutdown.Add(1)
go func() {
defer super.waitShutdown.Done()
- fail(super.RunProgram(ctx, ".", nil, nil, nginx,
- "-g", "error_log stderr info;",
- "-g", "pid "+filepath.Join(super.tempdir, "nginx.pid")+";",
- "-c", conffile))
+ fail(super.RunProgram(ctx, ".", runOptions{}, nginx, args...))
}()
- return waitForConnect(ctx, super.cluster.Services.Controller.ExternalURL.Host)
+ // Choose one of the ports where Nginx should listen, and wait
+ // here until we can connect. If ExternalURL is https://foo (with no port) then we connect to "foo:https"
+ testurl := url.URL(super.cluster.Services.Controller.ExternalURL)
+ if testurl.Port() == "" {
+ testurl.Host = net.JoinHostPort(testurl.Host, testurl.Scheme)
+ }
+ return waitForConnect(ctx, testurl.Host)
}