Skip to content

Commit

Permalink
report listening port when chosen by kernel (#770)
Browse files Browse the repository at this point in the history
Based off of the PR by @thesayyn
#720

Signed-off-by: Ramkumar Chinchani <[email protected]>
  • Loading branch information
rchincha authored Sep 9, 2022
1 parent d68bbf6 commit f3faae0
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 0 deletions.
1 change: 1 addition & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ var (
ErrSyncSignature = errors.New("sync: couldn't get upstream notary/cosign signatures")
ErrImageLintAnnotations = errors.New("routes: lint checks failed")
ErrParsingAuthHeader = errors.New("auth: failed parsing authorization header")
ErrBadType = errors.New("core: invalid type")
)
26 changes: 26 additions & 0 deletions pkg/api/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"os"
"runtime"
"strconv"
"strings"
goSync "sync"
"syscall"
Expand Down Expand Up @@ -41,6 +42,8 @@ type Controller struct {
Server *http.Server
Metrics monitoring.MetricServer
wgShutDown *goSync.WaitGroup // use it to gracefully shutdown goroutines
// runtime params
chosenPort int // kernel-chosen port
}

func NewController(config *config.Config) *Controller {
Expand Down Expand Up @@ -103,6 +106,10 @@ func DumpRuntimeParams(log log.Logger) {
evt.Msg("runtime params")
}

func (c *Controller) GetPort() int {
return c.chosenPort
}

func (c *Controller) Run(reloadCtx context.Context) error {
// print the current configuration, but strip secrets
c.Log.Info().Interface("params", c.Config.Sanitize()).Msg("configuration settings")
Expand Down Expand Up @@ -171,6 +178,25 @@ func (c *Controller) Run(reloadCtx context.Context) error {
return err
}

if c.Config.HTTP.Port == "0" || c.Config.HTTP.Port == "" {
chosenAddr, ok := listener.Addr().(*net.TCPAddr)
if !ok {
c.Log.Error().Str("port", c.Config.HTTP.Port).Msg("invalid addr type")

return errors.ErrBadType
}

c.chosenPort = chosenAddr.Port

c.Log.Info().Int("port", chosenAddr.Port).IPAddr("address", chosenAddr.IP).Msg(
"port is unspecified, listening on kernel chosen port",
)
} else {
chosenPort, _ := strconv.ParseInt(c.Config.HTTP.Port, 10, 64)

c.chosenPort = int(chosenPort)
}

if c.Config.HTTP.TLS != nil && c.Config.HTTP.TLS.Key != "" && c.Config.HTTP.TLS.Cert != "" {
server.TLSConfig = &tls.Config{
CipherSuites: []uint16{
Expand Down
49 changes: 49 additions & 0 deletions pkg/api/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,55 @@ func TestRunAlreadyRunningServer(t *testing.T) {
})
}

func TestAutoPortSelection(t *testing.T) {
Convey("Run server with specifying a port", t, func() {
conf := config.New()
conf.HTTP.Port = "0"

logFile, err := os.CreateTemp("", "zot-log*.txt")
So(err, ShouldBeNil)
conf.Log.Output = logFile.Name()
defer os.Remove(logFile.Name()) // clean up

ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = t.TempDir()

go startServer(ctlr)
time.Sleep(1000 * time.Millisecond)
defer stopServer(ctlr)

file, err := os.Open(logFile.Name())
So(err, ShouldBeNil)
defer file.Close()

scanner := bufio.NewScanner(file)

var contents bytes.Buffer
start := time.Now()

for scanner.Scan() {
if time.Since(start) < time.Second*30 {
t.Logf("Exhausted: Controller did not print the expected log within 30 seconds")
}
text := scanner.Text()
contents.WriteString(text)
if strings.Contains(text, "Port unspecified") {
break
}
t.Logf(scanner.Text())
}
So(scanner.Err(), ShouldBeNil)
So(contents.String(), ShouldContainSubstring,
"port is unspecified, listening on kernel chosen port",
)
So(contents.String(), ShouldContainSubstring, "\"address\":\"127.0.0.1\"")
So(contents.String(), ShouldContainSubstring, "\"port\":")

So(ctlr.GetPort(), ShouldBeGreaterThan, 0)
So(ctlr.GetPort(), ShouldBeLessThan, 65536)
})
}

func TestObjectStorageController(t *testing.T) {
skipIt(t)
Convey("Negative make a new object storage controller", t, func() {
Expand Down
20 changes: 20 additions & 0 deletions pkg/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -232,6 +233,10 @@ func validateStorageConfig(cfg *config.Config) error {
}

func validateConfiguration(config *config.Config) error {
if err := validateHTTP(config); err != nil {
return err
}

if err := validateGC(config); err != nil {
return err
}
Expand Down Expand Up @@ -514,6 +519,21 @@ func validateLDAP(config *config.Config) error {
return nil
}

func validateHTTP(config *config.Config) error {
if config.HTTP.Port != "" {
port, err := strconv.ParseInt(config.HTTP.Port, 10, 64)
if err != nil || (port < 0 || port > 65535) {
log.Error().Str("port", config.HTTP.Port).Msg("invalid port")

return errors.ErrBadConfig
}

fmt.Printf("HTTP port %d\n", port)
}

return nil
}

func validateGC(config *config.Config) error {
// enforce GC params
if config.Storage.GCDelay < 0 {
Expand Down
37 changes: 37 additions & 0 deletions pkg/cli/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,43 @@ func TestLoadConfig(t *testing.T) {
err = cli.LoadConfiguration(config, tmpfile.Name())
So(err, ShouldBeNil)
})

Convey("Test HTTP port", t, func() {
config := config.New()
tmpfile, err := os.CreateTemp("", "zot-test*.json")
So(err, ShouldBeNil)
defer os.Remove(tmpfile.Name())

content := []byte(`{"storage":{"rootDirectory":"/tmp/zot",
"subPaths": {"/a": {"rootDirectory": "/zot-a","dedupe":"true"},
"/b": {"rootDirectory": "/zot-a","dedupe":"true"}}},
"http":{"address":"127.0.0.1","port":"8080","realm":"zot",
"auth":{"htpasswd":{"path":"test/data/htpasswd"},"failDelay":1}}}`)
err = os.WriteFile(tmpfile.Name(), content, 0o0600)
So(err, ShouldBeNil)
err = cli.LoadConfiguration(config, tmpfile.Name())
So(err, ShouldBeNil)

content = []byte(`{"storage":{"rootDirectory":"/tmp/zot",
"subPaths": {"/a": {"rootDirectory": "/zot-a","dedupe":"true"},
"/b": {"rootDirectory": "/zot-a","dedupe":"true"}}},
"http":{"address":"127.0.0.1","port":"-1","realm":"zot",
"auth":{"htpasswd":{"path":"test/data/htpasswd"},"failDelay":1}}}`)
err = os.WriteFile(tmpfile.Name(), content, 0o0600)
So(err, ShouldBeNil)
err = cli.LoadConfiguration(config, tmpfile.Name())
So(err, ShouldNotBeNil)

content = []byte(`{"storage":{"rootDirectory":"/tmp/zot",
"subPaths": {"/a": {"rootDirectory": "/zot-a","dedupe":"true"},
"/b": {"rootDirectory": "/zot-a","dedupe":"true"}}},
"http":{"address":"127.0.0.1","port":"65536","realm":"zot",
"auth":{"htpasswd":{"path":"test/data/htpasswd"},"failDelay":1}}}`)
err = os.WriteFile(tmpfile.Name(), content, 0o0600)
So(err, ShouldBeNil)
err = cli.LoadConfiguration(config, tmpfile.Name())
So(err, ShouldNotBeNil)
})
}

func TestGC(t *testing.T) {
Expand Down

0 comments on commit f3faae0

Please sign in to comment.