From 9cd4d0cb850ea565d7289eb3df5e5562f28fbfb3 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:38:56 +0300 Subject: [PATCH 01/11] add edit_config api route (only logging_level supported) --- cmd/core/api.go | 32 ++++++++++++++++++++++++++++++++ cmd/core/display_utils.go | 18 ++++++++++++++++++ cmd/main.go | 15 ++------------- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/cmd/core/api.go b/cmd/core/api.go index b5ac0233..11ce9271 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "net" "net/http" "strconv" @@ -12,6 +13,7 @@ import ( eupfDocs "github.com/edgecomllc/eupf/cmd/docs" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" @@ -36,6 +38,7 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa v1.GET("config", DisplayConfig()) v1.GET("xdp_stats", DisplayXdpStatistics(forwardPlaneStats)) v1.GET("packet_stats", DisplayPacketStats(forwardPlaneStats)) + v1.POST("edit_config", EditConfig) qerMap := v1.Group("qer_map") { @@ -60,6 +63,35 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa return &ApiServer{router: router} } +type EditConfigRequest struct { + ConfigName string `json:"config_name"` + ConfigValue string `json:"config_value"` +} + +func EditConfig(c *gin.Context) { + var editConfigRequest EditConfigRequest + if err := c.BindJSON(&editConfigRequest); err != nil { + c.IndentedJSON(http.StatusBadRequest, gin.H{ + "message": "Request body should have config_name and config_value fields", + }) + return + } + switch editConfigRequest.ConfigName { + case "logging_level": + if err := ConfigureLoggerLevel(editConfigRequest.ConfigValue); err != nil { + c.IndentedJSON(http.StatusBadRequest, + gin.H{ + "message": fmt.Sprintf("Can't parse logging level: '%s'. Using '%s' level", + editConfigRequest.ConfigValue, zerolog.GlobalLevel().String()), + }) + } else { + c.Status(http.StatusOK) + } + default: + c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Unsupported config_name"}) + } +} + type PacketStats struct { RxArp uint64 `json:"rx_arp"` RxIcmp uint64 `json:"rx_icmp"` diff --git a/cmd/core/display_utils.go b/cmd/core/display_utils.go index 43ead1f8..ec04379f 100644 --- a/cmd/core/display_utils.go +++ b/cmd/core/display_utils.go @@ -3,14 +3,32 @@ package core import ( "encoding/binary" "fmt" + "os" "strings" "time" + "github.com/edgecomllc/eupf/cmd/config" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/wmnsk/go-pfcp/ie" "github.com/wmnsk/go-pfcp/message" ) +func InitLogger() { + output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006/01/02 15:04:05"} + log.Logger = zerolog.New(output).With().Timestamp().Logger() +} + +func ConfigureLoggerLevel(loggingLevel string) error { + if loglvl, err := zerolog.ParseLevel(loggingLevel); err == nil { + zerolog.SetGlobalLevel(loglvl) + config.Conf.LoggingLevel = zerolog.GlobalLevel().String() + } else { + return err + } + return nil +} + func writeLineTabbed(sb *strings.Builder, s string, tab int) { sb.WriteString(strings.Repeat(" ", tab)) sb.WriteString(s) diff --git a/cmd/main.go b/cmd/main.go index 3df77e0b..43ea930c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,7 +12,6 @@ import ( "github.com/cilium/ebpf/link" "github.com/edgecomllc/eupf/cmd/config" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/wmnsk/go-pfcp/message" ) @@ -27,7 +26,8 @@ func main() { // Warning: inefficient log writing. // As zerolog docs says: "Pretty logging on the console is made possible using the provided (but inefficient) zerolog.ConsoleWriter." - ConfigureLogger() + core.InitLogger() + core.ConfigureLoggerLevel(config.Conf.LoggingLevel) if err := ebpf.IncreaseResourceLimits(); err != nil { log.Fatal().Msgf("Can't increase resource limits: %s", err.Error()) @@ -136,14 +136,3 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags { return link.XDPGenericMode } } - -func ConfigureLogger() { - output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006/01/02 15:04:05"} - log.Logger = zerolog.New(output).With().Timestamp().Logger() - - if loglvl, err := zerolog.ParseLevel(config.Conf.LoggingLevel); err == nil { - zerolog.SetGlobalLevel(loglvl) - } else { - log.Warn().Msgf("Can't parse logging level: %s. Using \"info\" level.", err.Error()) - } -} From ffe07b80d278c764624b3fadb3c6ced65d37bb03 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:40:17 +0300 Subject: [PATCH 02/11] small edit --- cmd/core/api.go | 4 ++-- cmd/core/display_utils.go | 2 +- cmd/main.go | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/core/api.go b/cmd/core/api.go index 11ce9271..c22de0df 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -81,8 +81,8 @@ func EditConfig(c *gin.Context) { if err := ConfigureLoggerLevel(editConfigRequest.ConfigValue); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ - "message": fmt.Sprintf("Can't parse logging level: '%s'. Using '%s' level", - editConfigRequest.ConfigValue, zerolog.GlobalLevel().String()), + "message": fmt.Sprintf("Logger configuring error: %s. Using '%s' level", + err.Error(), zerolog.GlobalLevel().String()), }) } else { c.Status(http.StatusOK) diff --git a/cmd/core/display_utils.go b/cmd/core/display_utils.go index ec04379f..ba9e57c2 100644 --- a/cmd/core/display_utils.go +++ b/cmd/core/display_utils.go @@ -24,7 +24,7 @@ func ConfigureLoggerLevel(loggingLevel string) error { zerolog.SetGlobalLevel(loglvl) config.Conf.LoggingLevel = zerolog.GlobalLevel().String() } else { - return err + return fmt.Errorf("Can't parse logging level: '%s'", loggingLevel) } return nil } diff --git a/cmd/main.go b/cmd/main.go index 43ea930c..797a1391 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,6 +12,7 @@ import ( "github.com/cilium/ebpf/link" "github.com/edgecomllc/eupf/cmd/config" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/wmnsk/go-pfcp/message" ) @@ -27,7 +28,9 @@ func main() { // Warning: inefficient log writing. // As zerolog docs says: "Pretty logging on the console is made possible using the provided (but inefficient) zerolog.ConsoleWriter." core.InitLogger() - core.ConfigureLoggerLevel(config.Conf.LoggingLevel) + if err := core.ConfigureLoggerLevel(config.Conf.LoggingLevel); err != nil { + log.Warn().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String()) + } if err := ebpf.IncreaseResourceLimits(); err != nil { log.Fatal().Msgf("Can't increase resource limits: %s", err.Error()) From 0d8d6476a5d03e7e0265bed1be6db2cd7339fc5f Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:41:43 +0300 Subject: [PATCH 03/11] perfume refactoring --- cmd/core/api.go | 4 ++-- cmd/core/display_utils.go | 2 +- cmd/main.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/core/api.go b/cmd/core/api.go index c22de0df..469e1112 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -38,7 +38,7 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa v1.GET("config", DisplayConfig()) v1.GET("xdp_stats", DisplayXdpStatistics(forwardPlaneStats)) v1.GET("packet_stats", DisplayPacketStats(forwardPlaneStats)) - v1.POST("edit_config", EditConfig) + v1.POST("config", EditConfig) qerMap := v1.Group("qer_map") { @@ -78,7 +78,7 @@ func EditConfig(c *gin.Context) { } switch editConfigRequest.ConfigName { case "logging_level": - if err := ConfigureLoggerLevel(editConfigRequest.ConfigValue); err != nil { + if err := SetLoggerLevel(editConfigRequest.ConfigValue); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ "message": fmt.Sprintf("Logger configuring error: %s. Using '%s' level", diff --git a/cmd/core/display_utils.go b/cmd/core/display_utils.go index ba9e57c2..1a3619ba 100644 --- a/cmd/core/display_utils.go +++ b/cmd/core/display_utils.go @@ -19,7 +19,7 @@ func InitLogger() { log.Logger = zerolog.New(output).With().Timestamp().Logger() } -func ConfigureLoggerLevel(loggingLevel string) error { +func SetLoggerLevel(loggingLevel string) error { if loglvl, err := zerolog.ParseLevel(loggingLevel); err == nil { zerolog.SetGlobalLevel(loglvl) config.Conf.LoggingLevel = zerolog.GlobalLevel().String() diff --git a/cmd/main.go b/cmd/main.go index 797a1391..ac7e7d10 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -28,7 +28,7 @@ func main() { // Warning: inefficient log writing. // As zerolog docs says: "Pretty logging on the console is made possible using the provided (but inefficient) zerolog.ConsoleWriter." core.InitLogger() - if err := core.ConfigureLoggerLevel(config.Conf.LoggingLevel); err != nil { + if err := core.SetLoggerLevel(config.Conf.LoggingLevel); err != nil { log.Warn().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String()) } From 5f2a053276e22226405785a5475e9383fcfa9de4 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:42:32 +0300 Subject: [PATCH 04/11] monitoring config file changes (only logging_level supported) --- cmd/config/config.go | 20 ++++++++++++++++++++ cmd/config/startup.go | 15 +++++++++++++++ cmd/main.go | 17 +++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/cmd/config/config.go b/cmd/config/config.go index 395112a6..8e0a1872 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -86,8 +86,11 @@ func init() { v.SetEnvPrefix("upf") v.AutomaticEnv() + configFileAvailable = true + if err := v.ReadInConfig(); err != nil { log.Printf("Unable to read config file, %v", err) + configFileAvailable = false } log.Printf("Get raw config: %+v", v.AllSettings()) @@ -101,3 +104,20 @@ func (c *UpfConfig) Validate() error { func (c *UpfConfig) Unmarshal() error { return v.UnmarshalExact(c) } + +var configFileAvailable bool + +func IsConfigFileAvailable() bool { + return configFileAvailable +} + +func (c *UpfConfig) UnmarshalUpdatableKeys() error { + log.Println("Only logging_level parameter update in config file is supported yet") + keysFieldsMap := map[string]interface{}{"logging_level": &c.LoggingLevel} + for key := range keysFieldsMap { + if err := v.UnmarshalKey(key, keysFieldsMap[key]); err != nil { + return err + } + } + return nil +} diff --git a/cmd/config/startup.go b/cmd/config/startup.go index e677fd89..88fae1bf 100644 --- a/cmd/config/startup.go +++ b/cmd/config/startup.go @@ -18,3 +18,18 @@ func Init() { log.Printf("Apply eUPF config: %+v", Conf) } + +func ReloadOnChange() error { + if err := Conf.UnmarshalUpdatableKeys(); err != nil { + log.Fatalf("Unable to decode into struct, %v", err) + return err + } + + if err := Conf.Validate(); err != nil { + log.Fatalf("eUPF config is invalid: %v", err) + return err + } + + log.Printf("Apply updated eUPF config: %+v", Conf) + return nil +} diff --git a/cmd/main.go b/cmd/main.go index ac7e7d10..f1b7feab 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,8 +12,10 @@ import ( "github.com/cilium/ebpf/link" "github.com/edgecomllc/eupf/cmd/config" + "github.com/fsnotify/fsnotify" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/spf13/viper" "github.com/wmnsk/go-pfcp/message" ) @@ -107,6 +109,12 @@ func main() { } }() + // Handle config file change + if config.IsConfigFileAvailable() { + viper.OnConfigChange(OnConfigChange) + viper.WatchConfig() + } + // Print the contents of the BPF hash map (source IP address -> packet count). ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -139,3 +147,12 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags { return link.XDPGenericMode } } + +func OnConfigChange(e fsnotify.Event) { + if err := config.ReloadOnChange(); err == nil { + // For now only logging_level parameter update in config file is supported. + if err := core.SetLoggerLevel(config.Conf.LoggingLevel); err != nil { + log.Warn().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String()) + } + } +} From 107436d655329edd48c0a8158e73a723af283b69 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:52:25 +0300 Subject: [PATCH 05/11] sample commit to run github workflow jobs --- cmd/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index f1b7feab..9f0c13fe 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -109,7 +109,7 @@ func main() { } }() - // Handle config file change + // Handle config file change. if config.IsConfigFileAvailable() { viper.OnConfigChange(OnConfigChange) viper.WatchConfig() From ec045293892b713d9208bda5a11c226814c93d26 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 19:45:56 +0300 Subject: [PATCH 06/11] edit request body format --- cmd/core/api.go | 26 ++++++++++---------------- cmd/core/display_utils.go | 3 +++ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/cmd/core/api.go b/cmd/core/api.go index 469e1112..a624b026 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -64,31 +64,25 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa } type EditConfigRequest struct { - ConfigName string `json:"config_name"` - ConfigValue string `json:"config_value"` + LoggingLevel string `json:"logging_level"` } func EditConfig(c *gin.Context) { var editConfigRequest EditConfigRequest if err := c.BindJSON(&editConfigRequest); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ - "message": "Request body should have config_name and config_value fields", + "message": "Request body should have logging_level field", }) return } - switch editConfigRequest.ConfigName { - case "logging_level": - if err := SetLoggerLevel(editConfigRequest.ConfigValue); err != nil { - c.IndentedJSON(http.StatusBadRequest, - gin.H{ - "message": fmt.Sprintf("Logger configuring error: %s. Using '%s' level", - err.Error(), zerolog.GlobalLevel().String()), - }) - } else { - c.Status(http.StatusOK) - } - default: - c.IndentedJSON(http.StatusBadRequest, gin.H{"message": "Unsupported config_name"}) + if err := SetLoggerLevel(editConfigRequest.LoggingLevel); err != nil { + c.IndentedJSON(http.StatusBadRequest, + gin.H{ + "message": fmt.Sprintf("Logger configuring error: %s. Using '%s' level", + err.Error(), zerolog.GlobalLevel().String()), + }) + } else { + c.Status(http.StatusOK) } } diff --git a/cmd/core/display_utils.go b/cmd/core/display_utils.go index 1a3619ba..b6e1871c 100644 --- a/cmd/core/display_utils.go +++ b/cmd/core/display_utils.go @@ -20,6 +20,9 @@ func InitLogger() { } func SetLoggerLevel(loggingLevel string) error { + if loggingLevel == "" { + return fmt.Errorf("Logging level can't be empty") + } if loglvl, err := zerolog.ParseLevel(loggingLevel); err == nil { zerolog.SetGlobalLevel(loglvl) config.Conf.LoggingLevel = zerolog.GlobalLevel().String() From ca2a7ca1877dc97c92ef5a66c3d8f0ab414961f4 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:16:30 +0300 Subject: [PATCH 07/11] better error message --- cmd/core/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/core/api.go b/cmd/core/api.go index a624b026..07400691 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -71,7 +71,8 @@ func EditConfig(c *gin.Context) { var editConfigRequest EditConfigRequest if err := c.BindJSON(&editConfigRequest); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ - "message": "Request body should have logging_level field", + "message": "Request body json has incorrect format", + "correctFormat": EditConfigRequest{}, }) return } From 7cc7187e0772965e0448fb3f8402cbd25c885c82 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:02:36 +0300 Subject: [PATCH 08/11] refactored --- cmd/config/config.go | 47 +++++++++++++++++---------------------- cmd/config/startup.go | 15 +++++++------ cmd/core/api.go | 33 +++++++++++++-------------- cmd/core/config_edit.go | 18 +++++++++++++++ cmd/core/display_utils.go | 21 ----------------- cmd/core/logger.go | 28 +++++++++++++++++++++++ cmd/main.go | 21 ++++++++--------- config.yml | 1 + 8 files changed, 99 insertions(+), 85 deletions(-) create mode 100644 cmd/core/config_edit.go create mode 100644 cmd/core/logger.go create mode 100644 config.yml diff --git a/cmd/config/config.go b/cmd/config/config.go index 8e0a1872..6b2c7f2b 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -3,6 +3,7 @@ package config import ( "log" + "github.com/fsnotify/fsnotify" "github.com/go-playground/validator/v10" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -11,21 +12,21 @@ import ( var v = viper.GetViper() type UpfConfig struct { - InterfaceName []string `mapstructure:"interface_name"` - XDPAttachMode string `mapstructure:"xdp_attach_mode" validate:"oneof=generic native offload"` - ApiAddress string `mapstructure:"api_address" validate:"hostname_port"` - PfcpAddress string `mapstructure:"pfcp_address" validate:"hostname_port"` - PfcpNodeId string `mapstructure:"pfcp_node_id" validate:"hostname|ip"` - MetricsAddress string `mapstructure:"metrics_address" validate:"hostname_port"` - N3Address string `mapstructure:"n3_address" validate:"ipv4"` - QerMapSize uint32 `mapstructure:"qer_map_size" validate:"min=1"` - FarMapSize uint32 `mapstructure:"far_map_size" validate:"min=1"` - PdrMapSize uint32 `mapstructure:"pdr_map_size" validate:"min=1"` - EbpfMapResize bool `mapstructure:"resize_ebpf_maps"` - HeartbeatRetries uint32 `mapstructure:"heartbeat_retries"` - HeartbeatInterval uint32 `mapstructure:"heartbeat_interval"` - HeartbeatTimeout uint32 `mapstructure:"heartbeat_timeout"` - LoggingLevel string `mapstructure:"logging_level"` + InterfaceName []string `mapstructure:"interface_name" json:"interface_name"` + XDPAttachMode string `mapstructure:"xdp_attach_mode" validate:"oneof=generic native offload" json:"xdp_attach_mode"` + ApiAddress string `mapstructure:"api_address" validate:"hostname_port" json:"api_address"` + PfcpAddress string `mapstructure:"pfcp_address" validate:"hostname_port" json:"pfcp_address"` + PfcpNodeId string `mapstructure:"pfcp_node_id" validate:"hostname|ip" json:"pfcp_node_id"` + MetricsAddress string `mapstructure:"metrics_address" validate:"hostname_port" json:"metrics_address"` + N3Address string `mapstructure:"n3_address" validate:"ipv4" json:"n3_address"` + QerMapSize uint32 `mapstructure:"qer_map_size" validate:"min=1" json:"qer_map_size"` + FarMapSize uint32 `mapstructure:"far_map_size" validate:"min=1" json:"far_map_size"` + PdrMapSize uint32 `mapstructure:"pdr_map_size" validate:"min=1" json:"pdr_map_size"` + EbpfMapResize bool `mapstructure:"resize_ebpf_maps" json:"resize_ebpf_maps"` + HeartbeatRetries uint32 `mapstructure:"heartbeat_retries" json:"heartbeat_retries"` + HeartbeatInterval uint32 `mapstructure:"heartbeat_interval" json:"heartbeat_interval"` + HeartbeatTimeout uint32 `mapstructure:"heartbeat_timeout" json:"heartbeat_timeout"` + LoggingLevel string `mapstructure:"logging_level" validate:"required" json:"logging_level"` } func init() { @@ -107,17 +108,9 @@ func (c *UpfConfig) Unmarshal() error { var configFileAvailable bool -func IsConfigFileAvailable() bool { - return configFileAvailable -} - -func (c *UpfConfig) UnmarshalUpdatableKeys() error { - log.Println("Only logging_level parameter update in config file is supported yet") - keysFieldsMap := map[string]interface{}{"logging_level": &c.LoggingLevel} - for key := range keysFieldsMap { - if err := v.UnmarshalKey(key, keysFieldsMap[key]); err != nil { - return err - } +func SetWatchConfig(onConfigChange func(e fsnotify.Event)) { + if configFileAvailable { + viper.OnConfigChange(onConfigChange) + viper.WatchConfig() } - return nil } diff --git a/cmd/config/startup.go b/cmd/config/startup.go index 88fae1bf..e22fe8f7 100644 --- a/cmd/config/startup.go +++ b/cmd/config/startup.go @@ -19,17 +19,18 @@ func Init() { log.Printf("Apply eUPF config: %+v", Conf) } -func ReloadOnChange() error { - if err := Conf.UnmarshalUpdatableKeys(); err != nil { +func GetUpdatedConf() (UpfConfig, error) { + var updatedConf UpfConfig + if err := updatedConf.Unmarshal(); err != nil { log.Fatalf("Unable to decode into struct, %v", err) - return err + return UpfConfig{}, err } - if err := Conf.Validate(); err != nil { + if err := updatedConf.Validate(); err != nil { log.Fatalf("eUPF config is invalid: %v", err) - return err + return UpfConfig{}, err } - log.Printf("Apply updated eUPF config: %+v", Conf) - return nil + log.Printf("Apply updated eUPF config: %+v", updatedConf) + return updatedConf, nil } diff --git a/cmd/core/api.go b/cmd/core/api.go index 07400691..0222dec0 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -7,13 +7,12 @@ import ( "strconv" "unsafe" + "github.com/edgecomllc/eupf/cmd/config" "github.com/edgecomllc/eupf/cmd/ebpf" - "github.com/edgecomllc/eupf/cmd/config" eupfDocs "github.com/edgecomllc/eupf/cmd/docs" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" @@ -35,10 +34,14 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa v1 := router.Group("/api/v1") { v1.GET("upf_pipeline", ListUpfPipeline(bpfObjects)) - v1.GET("config", DisplayConfig()) v1.GET("xdp_stats", DisplayXdpStatistics(forwardPlaneStats)) v1.GET("packet_stats", DisplayPacketStats(forwardPlaneStats)) - v1.POST("config", EditConfig) + + config := v1.Group("config") + { + config.GET("", DisplayConfig()) + config.POST("", EditConfig) + } qerMap := v1.Group("qer_map") { @@ -63,25 +66,19 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa return &ApiServer{router: router} } -type EditConfigRequest struct { - LoggingLevel string `json:"logging_level"` -} - func EditConfig(c *gin.Context) { - var editConfigRequest EditConfigRequest - if err := c.BindJSON(&editConfigRequest); err != nil { + var conf config.UpfConfig + if err := c.BindJSON(&conf); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ - "message": "Request body json has incorrect format", - "correctFormat": EditConfigRequest{}, + "message": "Request body json has incorrect format. Use one or more fields from the following structure", + "correctFormat": config.UpfConfig{}, }) return } - if err := SetLoggerLevel(editConfigRequest.LoggingLevel); err != nil { - c.IndentedJSON(http.StatusBadRequest, - gin.H{ - "message": fmt.Sprintf("Logger configuring error: %s. Using '%s' level", - err.Error(), zerolog.GlobalLevel().String()), - }) + if err := SetConfig(conf); err != nil { + c.IndentedJSON(http.StatusBadRequest, gin.H{ + "message": fmt.Sprintf("Error during editing config: %s", err.Error()), + }) } else { c.Status(http.StatusOK) } diff --git a/cmd/core/config_edit.go b/cmd/core/config_edit.go new file mode 100644 index 00000000..b4101120 --- /dev/null +++ b/cmd/core/config_edit.go @@ -0,0 +1,18 @@ +package core + +import ( + "fmt" + + "github.com/edgecomllc/eupf/cmd/config" + + "github.com/rs/zerolog" +) + +func SetConfig(conf config.UpfConfig) error { + // For now only logging_level parameter update in config.UpfConfig is supported. + if err := SetLoggerLevel(conf.LoggingLevel); err != nil { + return fmt.Errorf("Logger configuring error: %s. Using '%s' level", + err.Error(), zerolog.GlobalLevel().String()) + } + return nil +} diff --git a/cmd/core/display_utils.go b/cmd/core/display_utils.go index b6e1871c..43ead1f8 100644 --- a/cmd/core/display_utils.go +++ b/cmd/core/display_utils.go @@ -3,35 +3,14 @@ package core import ( "encoding/binary" "fmt" - "os" "strings" "time" - "github.com/edgecomllc/eupf/cmd/config" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/wmnsk/go-pfcp/ie" "github.com/wmnsk/go-pfcp/message" ) -func InitLogger() { - output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006/01/02 15:04:05"} - log.Logger = zerolog.New(output).With().Timestamp().Logger() -} - -func SetLoggerLevel(loggingLevel string) error { - if loggingLevel == "" { - return fmt.Errorf("Logging level can't be empty") - } - if loglvl, err := zerolog.ParseLevel(loggingLevel); err == nil { - zerolog.SetGlobalLevel(loglvl) - config.Conf.LoggingLevel = zerolog.GlobalLevel().String() - } else { - return fmt.Errorf("Can't parse logging level: '%s'", loggingLevel) - } - return nil -} - func writeLineTabbed(sb *strings.Builder, s string, tab int) { sb.WriteString(strings.Repeat(" ", tab)) sb.WriteString(s) diff --git a/cmd/core/logger.go b/cmd/core/logger.go new file mode 100644 index 00000000..989da1b5 --- /dev/null +++ b/cmd/core/logger.go @@ -0,0 +1,28 @@ +package core + +import ( + "fmt" + "os" + + "github.com/edgecomllc/eupf/cmd/config" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +func InitLogger() { + output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006/01/02 15:04:05"} + log.Logger = zerolog.New(output).With().Timestamp().Logger() +} + +func SetLoggerLevel(loggingLevel string) error { + if loggingLevel == "" { + return fmt.Errorf("Logging level can't be empty") + } + if loglvl, err := zerolog.ParseLevel(loggingLevel); err == nil { + zerolog.SetGlobalLevel(loglvl) + config.Conf.LoggingLevel = zerolog.GlobalLevel().String() + } else { + return fmt.Errorf("Can't parse logging level: '%s'", loggingLevel) + } + return nil +} diff --git a/cmd/main.go b/cmd/main.go index 9f0c13fe..8b2076cb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,13 +9,12 @@ import ( "github.com/edgecomllc/eupf/cmd/core" "github.com/edgecomllc/eupf/cmd/ebpf" + "github.com/fsnotify/fsnotify" "github.com/cilium/ebpf/link" "github.com/edgecomllc/eupf/cmd/config" - "github.com/fsnotify/fsnotify" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/spf13/viper" "github.com/wmnsk/go-pfcp/message" ) @@ -31,7 +30,7 @@ func main() { // As zerolog docs says: "Pretty logging on the console is made possible using the provided (but inefficient) zerolog.ConsoleWriter." core.InitLogger() if err := core.SetLoggerLevel(config.Conf.LoggingLevel); err != nil { - log.Warn().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String()) + log.Error().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String()) } if err := ebpf.IncreaseResourceLimits(); err != nil { @@ -110,10 +109,7 @@ func main() { }() // Handle config file change. - if config.IsConfigFileAvailable() { - viper.OnConfigChange(OnConfigChange) - viper.WatchConfig() - } + config.SetWatchConfig(OnConfigFileChange) // Print the contents of the BPF hash map (source IP address -> packet count). ticker := time.NewTicker(5 * time.Second) @@ -148,11 +144,12 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags { } } -func OnConfigChange(e fsnotify.Event) { - if err := config.ReloadOnChange(); err == nil { - // For now only logging_level parameter update in config file is supported. - if err := core.SetLoggerLevel(config.Conf.LoggingLevel); err != nil { - log.Warn().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String()) +func OnConfigFileChange(e fsnotify.Event) { + if e.Has(fsnotify.Create) || e.Has(fsnotify.Write) { + if conf, err := config.GetUpdatedConf(); err == nil { + if err := core.SetConfig(conf); err != nil { + log.Error().Msgf("Error during config file change handling: %s", err.Error()) + } } } } diff --git a/config.yml b/config.yml new file mode 100644 index 00000000..b87e8a75 --- /dev/null +++ b/config.yml @@ -0,0 +1 @@ +logging_level: debu \ No newline at end of file From 9329f2dac62de7cf42d21aa8350631f2d1ce49c7 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:23:00 +0300 Subject: [PATCH 09/11] remove level change through config.yml for this PR --- cmd/config/config.go | 13 ------------- cmd/config/startup.go | 16 ---------------- cmd/main.go | 14 -------------- config.yml | 1 - 4 files changed, 44 deletions(-) delete mode 100644 config.yml diff --git a/cmd/config/config.go b/cmd/config/config.go index 6b2c7f2b..f6a4d783 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -3,7 +3,6 @@ package config import ( "log" - "github.com/fsnotify/fsnotify" "github.com/go-playground/validator/v10" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -87,11 +86,8 @@ func init() { v.SetEnvPrefix("upf") v.AutomaticEnv() - configFileAvailable = true - if err := v.ReadInConfig(); err != nil { log.Printf("Unable to read config file, %v", err) - configFileAvailable = false } log.Printf("Get raw config: %+v", v.AllSettings()) @@ -105,12 +101,3 @@ func (c *UpfConfig) Validate() error { func (c *UpfConfig) Unmarshal() error { return v.UnmarshalExact(c) } - -var configFileAvailable bool - -func SetWatchConfig(onConfigChange func(e fsnotify.Event)) { - if configFileAvailable { - viper.OnConfigChange(onConfigChange) - viper.WatchConfig() - } -} diff --git a/cmd/config/startup.go b/cmd/config/startup.go index e22fe8f7..e677fd89 100644 --- a/cmd/config/startup.go +++ b/cmd/config/startup.go @@ -18,19 +18,3 @@ func Init() { log.Printf("Apply eUPF config: %+v", Conf) } - -func GetUpdatedConf() (UpfConfig, error) { - var updatedConf UpfConfig - if err := updatedConf.Unmarshal(); err != nil { - log.Fatalf("Unable to decode into struct, %v", err) - return UpfConfig{}, err - } - - if err := updatedConf.Validate(); err != nil { - log.Fatalf("eUPF config is invalid: %v", err) - return UpfConfig{}, err - } - - log.Printf("Apply updated eUPF config: %+v", updatedConf) - return updatedConf, nil -} diff --git a/cmd/main.go b/cmd/main.go index 8b2076cb..bee52dd2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,7 +9,6 @@ import ( "github.com/edgecomllc/eupf/cmd/core" "github.com/edgecomllc/eupf/cmd/ebpf" - "github.com/fsnotify/fsnotify" "github.com/cilium/ebpf/link" "github.com/edgecomllc/eupf/cmd/config" @@ -108,9 +107,6 @@ func main() { } }() - // Handle config file change. - config.SetWatchConfig(OnConfigFileChange) - // Print the contents of the BPF hash map (source IP address -> packet count). ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -143,13 +139,3 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags { return link.XDPGenericMode } } - -func OnConfigFileChange(e fsnotify.Event) { - if e.Has(fsnotify.Create) || e.Has(fsnotify.Write) { - if conf, err := config.GetUpdatedConf(); err == nil { - if err := core.SetConfig(conf); err != nil { - log.Error().Msgf("Error during config file change handling: %s", err.Error()) - } - } - } -} diff --git a/config.yml b/config.yml deleted file mode 100644 index b87e8a75..00000000 --- a/config.yml +++ /dev/null @@ -1 +0,0 @@ -logging_level: debu \ No newline at end of file From bf04062f62565ce93f3deceb9335e3a8a58be380 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Tue, 17 Oct 2023 21:22:04 +0300 Subject: [PATCH 10/11] runtime modification using config file --- cmd/config/config.go | 13 +++++++++++++ cmd/config/startup.go | 16 ++++++++++++++++ cmd/main.go | 14 ++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/cmd/config/config.go b/cmd/config/config.go index f6a4d783..6b2c7f2b 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -3,6 +3,7 @@ package config import ( "log" + "github.com/fsnotify/fsnotify" "github.com/go-playground/validator/v10" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -86,8 +87,11 @@ func init() { v.SetEnvPrefix("upf") v.AutomaticEnv() + configFileAvailable = true + if err := v.ReadInConfig(); err != nil { log.Printf("Unable to read config file, %v", err) + configFileAvailable = false } log.Printf("Get raw config: %+v", v.AllSettings()) @@ -101,3 +105,12 @@ func (c *UpfConfig) Validate() error { func (c *UpfConfig) Unmarshal() error { return v.UnmarshalExact(c) } + +var configFileAvailable bool + +func SetWatchConfig(onConfigChange func(e fsnotify.Event)) { + if configFileAvailable { + viper.OnConfigChange(onConfigChange) + viper.WatchConfig() + } +} diff --git a/cmd/config/startup.go b/cmd/config/startup.go index e677fd89..e22fe8f7 100644 --- a/cmd/config/startup.go +++ b/cmd/config/startup.go @@ -18,3 +18,19 @@ func Init() { log.Printf("Apply eUPF config: %+v", Conf) } + +func GetUpdatedConf() (UpfConfig, error) { + var updatedConf UpfConfig + if err := updatedConf.Unmarshal(); err != nil { + log.Fatalf("Unable to decode into struct, %v", err) + return UpfConfig{}, err + } + + if err := updatedConf.Validate(); err != nil { + log.Fatalf("eUPF config is invalid: %v", err) + return UpfConfig{}, err + } + + log.Printf("Apply updated eUPF config: %+v", updatedConf) + return updatedConf, nil +} diff --git a/cmd/main.go b/cmd/main.go index bee52dd2..8b2076cb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -9,6 +9,7 @@ import ( "github.com/edgecomllc/eupf/cmd/core" "github.com/edgecomllc/eupf/cmd/ebpf" + "github.com/fsnotify/fsnotify" "github.com/cilium/ebpf/link" "github.com/edgecomllc/eupf/cmd/config" @@ -107,6 +108,9 @@ func main() { } }() + // Handle config file change. + config.SetWatchConfig(OnConfigFileChange) + // Print the contents of the BPF hash map (source IP address -> packet count). ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -139,3 +143,13 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags { return link.XDPGenericMode } } + +func OnConfigFileChange(e fsnotify.Event) { + if e.Has(fsnotify.Create) || e.Has(fsnotify.Write) { + if conf, err := config.GetUpdatedConf(); err == nil { + if err := core.SetConfig(conf); err != nil { + log.Error().Msgf("Error during config file change handling: %s", err.Error()) + } + } + } +} From ddb37b75cfacffd460e850b12419a1ae9c122035 Mon Sep 17 00:00:00 2001 From: kade-ddnkv <88973774+kade-ddnkv@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:20:40 +0300 Subject: [PATCH 11/11] fixes after review --- cmd/config/config.go | 10 ++++------ cmd/config/startup.go | 12 ++++++------ cmd/core/api.go | 6 +++--- cmd/main.go | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index 6b2c7f2b..2f7c2d80 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -87,11 +87,9 @@ func init() { v.SetEnvPrefix("upf") v.AutomaticEnv() - configFileAvailable = true - if err := v.ReadInConfig(); err != nil { log.Printf("Unable to read config file, %v", err) - configFileAvailable = false + isConfigFileAvailable = false } log.Printf("Get raw config: %+v", v.AllSettings()) @@ -106,10 +104,10 @@ func (c *UpfConfig) Unmarshal() error { return v.UnmarshalExact(c) } -var configFileAvailable bool +var isConfigFileAvailable bool = true -func SetWatchConfig(onConfigChange func(e fsnotify.Event)) { - if configFileAvailable { +func WatchConfig(onConfigChange func(e fsnotify.Event)) { + if isConfigFileAvailable { viper.OnConfigChange(onConfigChange) viper.WatchConfig() } diff --git a/cmd/config/startup.go b/cmd/config/startup.go index e22fe8f7..4acf8bc8 100644 --- a/cmd/config/startup.go +++ b/cmd/config/startup.go @@ -19,18 +19,18 @@ func Init() { log.Printf("Apply eUPF config: %+v", Conf) } -func GetUpdatedConf() (UpfConfig, error) { - var updatedConf UpfConfig - if err := updatedConf.Unmarshal(); err != nil { +func ReadConfig() (UpfConfig, error) { + var conf UpfConfig + if err := conf.Unmarshal(); err != nil { log.Fatalf("Unable to decode into struct, %v", err) return UpfConfig{}, err } - if err := updatedConf.Validate(); err != nil { + if err := conf.Validate(); err != nil { log.Fatalf("eUPF config is invalid: %v", err) return UpfConfig{}, err } - log.Printf("Apply updated eUPF config: %+v", updatedConf) - return updatedConf, nil + log.Printf("Read config: %+v", conf) + return conf, nil } diff --git a/cmd/core/api.go b/cmd/core/api.go index 0222dec0..01dd3678 100644 --- a/cmd/core/api.go +++ b/cmd/core/api.go @@ -40,7 +40,7 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa config := v1.Group("config") { config.GET("", DisplayConfig()) - config.POST("", EditConfig) + config.POST("", UpdateConfig) } qerMap := v1.Group("qer_map") @@ -66,7 +66,7 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa return &ApiServer{router: router} } -func EditConfig(c *gin.Context) { +func UpdateConfig(c *gin.Context) { var conf config.UpfConfig if err := c.BindJSON(&conf); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ @@ -77,7 +77,7 @@ func EditConfig(c *gin.Context) { } if err := SetConfig(conf); err != nil { c.IndentedJSON(http.StatusBadRequest, gin.H{ - "message": fmt.Sprintf("Error during editing config: %s", err.Error()), + "message": fmt.Sprintf("Error during updating config: %s", err.Error()), }) } else { c.Status(http.StatusOK) diff --git a/cmd/main.go b/cmd/main.go index 8b2076cb..064921e7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -109,7 +109,7 @@ func main() { }() // Handle config file change. - config.SetWatchConfig(OnConfigFileChange) + config.WatchConfig(OnConfigFileChange) // Print the contents of the BPF hash map (source IP address -> packet count). ticker := time.NewTicker(5 * time.Second) @@ -146,7 +146,7 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags { func OnConfigFileChange(e fsnotify.Event) { if e.Has(fsnotify.Create) || e.Has(fsnotify.Write) { - if conf, err := config.GetUpdatedConf(); err == nil { + if conf, err := config.ReadConfig(); err == nil { if err := core.SetConfig(conf); err != nil { log.Error().Msgf("Error during config file change handling: %s", err.Error()) }