feat: add new telemetry server (#254)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
This commit is contained in:
Márk Sági-Kazár
2020-12-18 09:01:19 +01:00
committed by GitHub
parent 65e6d5475f
commit 6d895843dc
5 changed files with 128 additions and 8 deletions

View File

@@ -16,6 +16,7 @@ import (
"github.com/drakkan/sftpgo/kms" "github.com/drakkan/sftpgo/kms"
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/sftpd" "github.com/drakkan/sftpgo/sftpd"
"github.com/drakkan/sftpgo/telemetry"
"github.com/drakkan/sftpgo/utils" "github.com/drakkan/sftpgo/utils"
"github.com/drakkan/sftpgo/version" "github.com/drakkan/sftpgo/version"
"github.com/drakkan/sftpgo/webdavd" "github.com/drakkan/sftpgo/webdavd"
@@ -38,14 +39,15 @@ var (
) )
type globalConfig struct { type globalConfig struct {
Common common.Configuration `json:"common" mapstructure:"common"` Common common.Configuration `json:"common" mapstructure:"common"`
SFTPD sftpd.Configuration `json:"sftpd" mapstructure:"sftpd"` SFTPD sftpd.Configuration `json:"sftpd" mapstructure:"sftpd"`
FTPD ftpd.Configuration `json:"ftpd" mapstructure:"ftpd"` FTPD ftpd.Configuration `json:"ftpd" mapstructure:"ftpd"`
WebDAVD webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"` WebDAVD webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
ProviderConf dataprovider.Config `json:"data_provider" mapstructure:"data_provider"` ProviderConf dataprovider.Config `json:"data_provider" mapstructure:"data_provider"`
HTTPDConfig httpd.Conf `json:"httpd" mapstructure:"httpd"` HTTPDConfig httpd.Conf `json:"httpd" mapstructure:"httpd"`
HTTPConfig httpclient.Config `json:"http" mapstructure:"http"` HTTPConfig httpclient.Config `json:"http" mapstructure:"http"`
KMSConfig kms.Configuration `json:"kms" mapstructure:"kms"` KMSConfig kms.Configuration `json:"kms" mapstructure:"kms"`
TelemetryConfig telemetry.Conf `json:"telemetry" mapstructure:"telemetry"`
} }
func init() { func init() {
@@ -182,6 +184,10 @@ func Init() {
MasterKeyPath: "", MasterKeyPath: "",
}, },
}, },
TelemetryConfig: telemetry.Conf{
BindPort: 10000,
BindAddress: "127.0.0.1",
},
} }
viper.SetEnvPrefix(configEnvPrefix) viper.SetEnvPrefix(configEnvPrefix)
@@ -268,6 +274,16 @@ func SetKMSConfig(config kms.Configuration) {
globalConf.KMSConfig = config globalConf.KMSConfig = config
} }
// GetTelemetryConfig returns the telemetry configuration
func GetTelemetryConfig() telemetry.Conf {
return globalConf.TelemetryConfig
}
// SetTelemetryConfig sets the telemetry configuration
func SetTelemetryConfig(config telemetry.Conf) {
globalConf.TelemetryConfig = config
}
// HasServicesToStart returns true if the config defines at least a service to start. // HasServicesToStart returns true if the config defines at least a service to start.
// Supported services are SFTP, FTP and WebDAV // Supported services are SFTP, FTP and WebDAV
func HasServicesToStart() bool { func HasServicesToStart() bool {
@@ -496,4 +512,6 @@ func setViperDefaults() {
viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify) viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify)
viper.SetDefault("kms.secrets.url", globalConf.KMSConfig.Secrets.URL) viper.SetDefault("kms.secrets.url", globalConf.KMSConfig.Secrets.URL)
viper.SetDefault("kms.secrets.master_key_path", globalConf.KMSConfig.Secrets.MasterKeyPath) viper.SetDefault("kms.secrets.master_key_path", globalConf.KMSConfig.Secrets.MasterKeyPath)
viper.SetDefault("telemetry.bind_port", globalConf.TelemetryConfig.BindPort)
viper.SetDefault("telemetry.bind_address", globalConf.TelemetryConfig.BindAddress)
} }

View File

@@ -315,6 +315,12 @@ func TestSetGetConfig(t *testing.T) {
config.SetKMSConfig(kmsConf) config.SetKMSConfig(kmsConf)
assert.Equal(t, kmsConf.Secrets.MasterKeyPath, config.GetKMSConfig().Secrets.MasterKeyPath) assert.Equal(t, kmsConf.Secrets.MasterKeyPath, config.GetKMSConfig().Secrets.MasterKeyPath)
assert.Equal(t, kmsConf.Secrets.URL, config.GetKMSConfig().Secrets.URL) assert.Equal(t, kmsConf.Secrets.URL, config.GetKMSConfig().Secrets.URL)
telemetryConf := config.GetTelemetryConfig()
telemetryConf.BindPort = 10001
telemetryConf.BindAddress = "0.0.0.0"
config.SetTelemetryConfig(telemetryConf)
assert.Equal(t, telemetryConf.BindPort, config.GetTelemetryConfig().BindPort)
assert.Equal(t, telemetryConf.BindAddress, config.GetTelemetryConfig().BindAddress)
} }
func TestServiceToStart(t *testing.T) { func TestServiceToStart(t *testing.T) {

View File

@@ -128,6 +128,7 @@ func (s *Service) startServices() {
ftpdConf := config.GetFTPDConfig() ftpdConf := config.GetFTPDConfig()
httpdConf := config.GetHTTPDConfig() httpdConf := config.GetHTTPDConfig()
webDavDConf := config.GetWebDAVDConfig() webDavDConf := config.GetWebDAVDConfig()
telemetryConf := config.GetTelemetryConfig()
if sftpdConf.BindPort > 0 { if sftpdConf.BindPort > 0 {
go func() { go func() {
@@ -182,6 +183,21 @@ func (s *Service) startServices() {
} else { } else {
logger.Debug(logSender, "", "WebDAV server not started, disabled in config file") logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
} }
if telemetryConf.BindPort > 0 {
go func() {
if err := telemetryConf.Initialize(s.Profiler); err != nil {
logger.Error(logSender, "", "could not start telemetry server: %v", err)
logger.ErrorToConsole("could not start telemetry server: %v", err)
s.Error = err
}
s.Shutdown <- true
}()
} else {
logger.Debug(logSender, "", "telemetry server not started, disabled in config file")
if s.PortableMode != 1 {
logger.DebugToConsole("telemetry server not started, disabled in config file")
}
}
} }
// Wait blocks until the service exits // Wait blocks until the service exits

32
telemetry/router.go Normal file
View File

@@ -0,0 +1,32 @@
package telemetry
import (
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/render"
"github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/metrics"
)
func initializeRouter(enableProfiler bool) {
router = chi.NewRouter()
router.Use(middleware.Recoverer)
router.Group(func(r chi.Router) {
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
render.PlainText(w, r, "ok")
})
})
metrics.AddMetricsEndpoint(metricsPath, router)
if enableProfiler {
logger.InfoToConsole("enabling the built-in profiler")
logger.Info(logSender, "", "enabling the built-in profiler")
router.Mount(pprofBasePath, middleware.Profiler())
}
}

48
telemetry/telemetry.go Normal file
View File

@@ -0,0 +1,48 @@
// Package telemetry provides telemetry information for SFTPGo, such as:
// - health information (for health checks)
// - metrics
// - profiling information
package telemetry
import (
"fmt"
"net/http"
"time"
"github.com/go-chi/chi"
"github.com/drakkan/sftpgo/logger"
)
const (
logSender = "telemetry"
metricsPath = "/metrics"
pprofBasePath = "/debug"
)
var (
router *chi.Mux
)
// Conf telemetry server configuration.
type Conf struct {
// The port used for serving HTTP requests. 0 disable the HTTP server. Default: 8080
BindPort int `json:"bind_port" mapstructure:"bind_port"`
// The address to listen on. A blank value means listen on all available network interfaces. Default: "127.0.0.1"
BindAddress string `json:"bind_address" mapstructure:"bind_address"`
}
// Initialize configures and starts the telemetry server.
func (c Conf) Initialize(enableProfiler bool) error {
logger.Debug(logSender, "", "initializing telemetry server with config %+v", c)
initializeRouter(enableProfiler)
httpServer := &http.Server{
Addr: fmt.Sprintf("%s:%d", c.BindAddress, c.BindPort),
Handler: router,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 16, // 64KB
}
return httpServer.ListenAndServe()
}