diff --git a/CHANGELOG.md b/CHANGELOG.md index f56595b0..962759ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,36 @@ See also the [v0.107.37 GitHub milestone][ms-v0.107.37]. NOTE: Add new changes BELOW THIS COMMENT. --> +### Added + +- The ability to set the port for the `pprof` debug API, see configuration + changes below. + +### Changed + +#### Configuration Changes + +In this release, the schema version has changed from 24 to 25. + +- Property `debug_pprof` which used to setup profiling HTTP handler, is now + moved to the new `pprof` object under `http` section. The new object contains + properties `enabled` and `port`: + + ```yaml + # BEFORE: + 'debug_pprof': true + + # AFTER: + 'http': + 'pprof': + 'enabled': true + 'port': 6060 + ``` + + Note that the new default `6060` is used as default. To rollback this change, + remove the new object `pprof`, set back `debug_pprof`, and change the + `schema_version` back to `24`. + diff --git a/internal/home/config.go b/internal/home/config.go index 361fdebb..b2161373 100644 --- a/internal/home/config.go +++ b/internal/home/config.go @@ -114,8 +114,6 @@ type configuration struct { Language string `yaml:"language"` // Theme is a UI theme for current user. Theme Theme `yaml:"theme"` - // DebugPProf defines if the profiling HTTP handler will listen on :6060. - DebugPProf bool `yaml:"debug_pprof"` DNS dnsConfig `yaml:"dns"` TLS tlsConfigSettings `yaml:"tls"` @@ -155,6 +153,9 @@ type configuration struct { // Field ordering is important, YAML fields better not to be reordered, if it's // not absolutely necessary. type httpConfig struct { + // Pprof defines the profiling HTTP handler. + Pprof *httpPprofConfig `yaml:"pprof"` + // Address is the address to serve the web UI on. Address netip.AddrPort @@ -163,6 +164,15 @@ type httpConfig struct { SessionTTL timeutil.Duration `yaml:"session_ttl"` } +// httpPprofConfig is the block with pprof HTTP configuration. +type httpPprofConfig struct { + // Port for the profiling handler. + Port uint16 `yaml:"port"` + + // Enabled defines if the profiling handler is enabled. + Enabled bool `yaml:"enabled"` +} + // dnsConfig is a block with DNS configuration params. // // Field ordering is important, YAML fields better not to be reordered, if it's @@ -277,6 +287,10 @@ var config = &configuration{ HTTPConfig: httpConfig{ Address: netip.AddrPortFrom(netip.IPv4Unspecified(), 3000), SessionTTL: timeutil.Duration{Duration: 30 * timeutil.Day}, + Pprof: &httpPprofConfig{ + Enabled: false, + Port: 6060, + }, }, DNS: dnsConfig{ BindHosts: []netip.Addr{netip.IPv4Unspecified()}, diff --git a/internal/home/home.go b/internal/home/home.go index 60eb63dc..a4f5c9e5 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -567,9 +567,8 @@ func run(opts options, clientBuildFS fs.FS) { err = config.write() fatalOnError(err) - if config.DebugPProf { - // TODO(a.garipov): Make the address configurable. - startPprof("localhost:6060") + if config.HTTPConfig.Pprof.Enabled { + startPprof(config.HTTPConfig.Pprof.Port) } } diff --git a/internal/home/upgrade.go b/internal/home/upgrade.go index be9e2873..4f3fb565 100644 --- a/internal/home/upgrade.go +++ b/internal/home/upgrade.go @@ -23,7 +23,7 @@ import ( ) // currentSchemaVersion is the current schema version. -const currentSchemaVersion = 24 +const currentSchemaVersion = 25 // These aliases are provided for convenience. type ( @@ -99,6 +99,7 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) { upgradeSchema21to22, upgradeSchema22to23, upgradeSchema23to24, + upgradeSchema24to25, } n := 0 @@ -1380,6 +1381,50 @@ func upgradeSchema23to24(diskConf yobj) (err error) { return nil } +// upgradeSchema24to25 performs the following changes: +// +// # BEFORE: +// 'debug_pprof': true +// +// # AFTER: +// 'http': +// 'pprof': +// 'enabled': true +// 'port': 6060 +func upgradeSchema24to25(diskConf yobj) (err error) { + log.Printf("Upgrade yaml: 24 to 25") + diskConf["schema_version"] = 25 + + debugPprofVal, ok := diskConf["debug_pprof"] + if !ok { + return nil + } + + debugPprofEnabled, ok := debugPprofVal.(bool) + if !ok { + return fmt.Errorf("unexpected type of debug_pprof: %T", debugPprofVal) + } + + httpVal, ok := diskConf["http"] + if !ok { + return nil + } + + httpObj, ok := httpVal.(yobj) + if !ok { + return fmt.Errorf("unexpected type of dns: %T", httpVal) + } + + httpObj["pprof"] = yobj{ + "enabled": debugPprofEnabled, + "port": 6060, + } + + delete(diskConf, "debug_pprof") + + return nil +} + // moveField gets field value for key from diskConf, and then set this value // in newConf for newKey. func moveField[T any](diskConf, newConf yobj, key, newKey string) (err error) { diff --git a/internal/home/upgrade_test.go b/internal/home/upgrade_test.go index a440ccfc..c6dd56c0 100644 --- a/internal/home/upgrade_test.go +++ b/internal/home/upgrade_test.go @@ -1379,3 +1379,90 @@ func TestUpgradeSchema23to24(t *testing.T) { }) } } + +func TestUpgradeSchema24to25(t *testing.T) { + const newSchemaVer = 25 + + testCases := []struct { + in yobj + want yobj + name string + wantErrMsg string + }{{ + name: "empty", + in: yobj{}, + want: yobj{ + "schema_version": newSchemaVer, + }, + wantErrMsg: "", + }, { + name: "ok", + in: yobj{ + "http": yobj{ + "address": "0.0.0.0:3000", + "session_ttl": "720h", + }, + "debug_pprof": true, + }, + want: yobj{ + "http": yobj{ + "address": "0.0.0.0:3000", + "session_ttl": "720h", + "pprof": yobj{ + "enabled": true, + "port": 6060, + }, + }, + "schema_version": newSchemaVer, + }, + wantErrMsg: "", + }, { + name: "ok_disabled", + in: yobj{ + "http": yobj{ + "address": "0.0.0.0:3000", + "session_ttl": "720h", + }, + "debug_pprof": false, + }, + want: yobj{ + "http": yobj{ + "address": "0.0.0.0:3000", + "session_ttl": "720h", + "pprof": yobj{ + "enabled": false, + "port": 6060, + }, + }, + "schema_version": newSchemaVer, + }, + wantErrMsg: "", + }, { + name: "invalid", + in: yobj{ + "http": yobj{ + "address": "0.0.0.0:3000", + "session_ttl": "720h", + }, + "debug_pprof": 1, + }, + want: yobj{ + "http": yobj{ + "address": "0.0.0.0:3000", + "session_ttl": "720h", + }, + "debug_pprof": 1, + "schema_version": newSchemaVer, + }, + wantErrMsg: "unexpected type of debug_pprof: int", + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := upgradeSchema24to25(tc.in) + testutil.AssertErrorMsg(t, tc.wantErrMsg, err) + + assert.Equal(t, tc.want, tc.in) + }) + } +} diff --git a/internal/home/web.go b/internal/home/web.go index 6649dfe2..ac659a3f 100644 --- a/internal/home/web.go +++ b/internal/home/web.go @@ -312,8 +312,10 @@ func (web *webAPI) mustStartHTTP3(address string) { } } -// startPprof launches the debug and profiling server on addr. -func startPprof(addr string) { +// startPprof launches the debug and profiling server on the provided port. +func startPprof(port uint16) { + addr := netip.AddrPortFrom(netutil.IPv4Localhost(), port) + runtime.SetBlockProfileRate(1) runtime.SetMutexProfileFraction(1) @@ -324,7 +326,7 @@ func startPprof(addr string) { defer log.OnPanic("pprof server") log.Info("pprof: listening on %q", addr) - err := http.ListenAndServe(addr, mux) + err := http.ListenAndServe(addr.String(), mux) if !errors.Is(err, http.ErrServerClosed) { log.Error("pprof: shutting down: %s", err) }