Skip to content

Commit d9d383c

Browse files
authored
Merge pull request #2206 from sthaha/fix-server-address
feat(config): add configurable web server listen addresses
2 parents 4a37dc7 + c9d7667 commit d9d383c

File tree

7 files changed

+640
-4
lines changed

7 files changed

+640
-4
lines changed

cmd/kepler/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ func createServices(logger *slog.Logger, cfg *config.Config) ([]service.Service,
158158

159159
apiServer := server.NewAPIServer(
160160
server.WithLogger(logger),
161+
server.WithListenAddress(cfg.Web.ListenAddresses),
161162
server.WithWebConfig(cfg.Web.Config),
162163
)
163164

compose/dev/kepler-dev/etc/kepler/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ debug: # debug related config
5353

5454
web:
5555
configFile: "" # Path to TLS server config file
56+
listenAddresses: # Web server listen addresses
57+
- :28282
5658

5759
kube: # kubernetes related config
5860
enabled: false # enable kubernetes monitoring (default: false)

config/config.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ package config
66
import (
77
"fmt"
88
"io"
9+
"net"
910
"os"
11+
"strconv"
1012
"strings"
1113
"time"
1214

@@ -39,7 +41,8 @@ type (
3941
} `yaml:"fake-cpu-meter"`
4042
}
4143
Web struct {
42-
Config string `yaml:"configFile"`
44+
Config string `yaml:"configFile"`
45+
ListenAddresses []string `yaml:"listenAddresses"`
4346
}
4447

4548
Monitor struct {
@@ -157,7 +160,8 @@ const (
157160

158161
pprofEnabledFlag = "debug.pprof"
159162

160-
WebConfigFlag = "web.config-file"
163+
WebConfigFlag = "web.config-file"
164+
WebListenAddressFlag = "web.listen-address"
161165

162166
// Exporters
163167
ExporterStdoutEnabledFlag = "exporter.stdout"
@@ -210,6 +214,9 @@ func DefaultConfig() *Config {
210214
Enabled: ptr.To(false),
211215
},
212216
},
217+
Web: Web{
218+
ListenAddresses: []string{":28282"},
219+
},
213220
Kube: Kube{
214221
Enabled: ptr.To(false),
215222
},
@@ -295,6 +302,7 @@ func RegisterFlags(app *kingpin.Application) ConfigUpdaterFn {
295302

296303
enablePprof := app.Flag(pprofEnabledFlag, "Enable pprof debug endpoints").Default("false").Bool()
297304
webConfig := app.Flag(WebConfigFlag, "Web config file path").Default("").String()
305+
webListenAddresses := app.Flag(WebListenAddressFlag, "Web server listen addresses").Default(":28282").Strings()
298306

299307
// exporters
300308
stdoutExporterEnabled := app.Flag(ExporterStdoutEnabledFlag, "Enable stdout exporter").Default("false").Bool()
@@ -343,6 +351,10 @@ func RegisterFlags(app *kingpin.Application) ConfigUpdaterFn {
343351
cfg.Web.Config = *webConfig
344352
}
345353

354+
if flagsSet[WebListenAddressFlag] {
355+
cfg.Web.ListenAddresses = *webListenAddresses
356+
}
357+
346358
if flagsSet[ExporterStdoutEnabledFlag] {
347359
cfg.Exporter.Stdout.Enabled = stdoutExporterEnabled
348360
}
@@ -378,6 +390,9 @@ func (c *Config) sanitize() {
378390
c.Host.SysFS = strings.TrimSpace(c.Host.SysFS)
379391
c.Host.ProcFS = strings.TrimSpace(c.Host.ProcFS)
380392
c.Web.Config = strings.TrimSpace(c.Web.Config)
393+
for i := range c.Web.ListenAddresses {
394+
c.Web.ListenAddresses[i] = strings.TrimSpace(c.Web.ListenAddresses[i])
395+
}
381396

382397
for i := range c.Rapl.Zones {
383398
c.Rapl.Zones[i] = strings.TrimSpace(c.Rapl.Zones[i])
@@ -437,6 +452,20 @@ func (c *Config) Validate(skips ...SkipValidation) error {
437452
}
438453
}
439454
}
455+
{ // Web listen addresses
456+
if len(c.Web.ListenAddresses) == 0 {
457+
errs = append(errs, "at least one web listen address must be specified")
458+
}
459+
for _, addr := range c.Web.ListenAddresses {
460+
if addr == "" {
461+
errs = append(errs, "web listen address cannot be empty")
462+
continue
463+
}
464+
if err := validateListenAddress(addr); err != nil {
465+
errs = append(errs, fmt.Sprintf("invalid web listen address %q: %s", addr, err.Error()))
466+
}
467+
}
468+
}
440469
{ // Monitor
441470
if c.Monitor.Interval < 0 {
442471
errs = append(errs, fmt.Sprintf("invalid monitor interval: %s can't be negative", c.Monitor.Interval))
@@ -506,6 +535,37 @@ func canReadFile(path string) error {
506535
return nil
507536
}
508537

538+
func validateListenAddress(addr string) error {
539+
if addr == "" {
540+
return fmt.Errorf("address cannot be empty")
541+
}
542+
543+
// Use Go's standard library to parse host:port
544+
_, port, err := net.SplitHostPort(addr)
545+
if err != nil {
546+
return fmt.Errorf("invalid address format: %w", err)
547+
}
548+
549+
// Validate port (host can be empty for listening on all interfaces)
550+
if err := validatePort(port); err != nil {
551+
return err
552+
}
553+
554+
return nil
555+
}
556+
557+
func validatePort(port string) error {
558+
portNum, err := strconv.Atoi(port)
559+
if err != nil {
560+
return fmt.Errorf("port must be numeric, got %s", port)
561+
}
562+
563+
if portNum < 1 || portNum > 65535 {
564+
return fmt.Errorf("port must be between 1 and 65535, got %d", portNum)
565+
}
566+
return nil
567+
}
568+
509569
func (c *Config) String() string {
510570
bytes, err := yaml.Marshal(c)
511571
if err == nil {

0 commit comments

Comments
 (0)