diff --git a/rocketpool-cli/node/status.go b/rocketpool-cli/node/status.go index 7f89bc388..c91ba361e 100644 --- a/rocketpool-cli/node/status.go +++ b/rocketpool-cli/node/status.go @@ -394,21 +394,6 @@ func getStatus(c *cli.Context) error { fmt.Println("The node is not registered with Rocket Pool.") } - // Alerts - if cfg.EnableMetrics.Value == true && len(status.Alerts) > 0 { - // only print alerts if enabled; to avoid misleading the user to thinking everything is fine (since we really don't know). - fmt.Printf("\n%s=== Alerts ===%s\n", colorGreen, colorReset) - for i, alert := range status.Alerts { - fmt.Println(alert.ColorString()) - if i == maxAlertItems-1 { - break - } - } - if len(status.Alerts) > maxAlertItems { - fmt.Printf("... and %d more.\n", len(status.Alerts)-maxAlertItems) - } - } - if status.Warning != "" { fmt.Printf("\n%sWARNING: %s%s\n", colorRed, status.Warning, colorReset) } diff --git a/rocketpool-cli/rocketpool-cli.go b/rocketpool-cli/rocketpool-cli.go index 87b6fffcd..b41470a46 100644 --- a/rocketpool-cli/rocketpool-cli.go +++ b/rocketpool-cli/rocketpool-cli.go @@ -18,9 +18,20 @@ import ( "github.com/rocket-pool/smartnode/rocketpool-cli/service" "github.com/rocket-pool/smartnode/rocketpool-cli/wallet" "github.com/rocket-pool/smartnode/shared" + "github.com/rocket-pool/smartnode/shared/services" + "github.com/rocket-pool/smartnode/shared/services/alerting" + "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" ) +const ( + colorReset string = "\033[0m" + colorRed string = "\033[31m" + colorGreen string = "\033[32m" + colorYellow string = "\033[33m" + maxAlertItems int = 3 +) + // Run func main() { @@ -123,11 +134,46 @@ A special thanks to the Rocket Pool community for all their contributions. return nil } + app.After = func(c *cli.Context) error { + + // Create Rocket Pool client + rp := rocketpool.NewClientFromCtx(c) + defer rp.Close() + + cfg, err := services.GetConfig(c) + if err != nil { + return err + } + + // Fetch node alerts + alerts, err := alerting.FetchNodeAlerts(cfg) + if err != nil { + return err + } + + // Display alerts if any exist + if len(alerts) > 0 { + fmt.Printf("\n%s=== Alerts ===%s\n", colorGreen, colorReset) + for i, alert := range alerts { + fmt.Println(alert.ColorString()) + if i == maxAlertItems-1 { + break + } + } + if len(alerts) > maxAlertItems { + fmt.Printf("... and %d more.\n", len(alerts)-maxAlertItems) + } + } + + return nil + } + // Run application fmt.Println("") if err := app.Run(os.Args); err != nil { cliutils.PrettyPrintError(err) } + fmt.Println("") } diff --git a/rocketpool/api/node/status.go b/rocketpool/api/node/status.go index 8eaa2070d..2115cb1e4 100644 --- a/rocketpool/api/node/status.go +++ b/rocketpool/api/node/status.go @@ -25,8 +25,6 @@ import ( mp "github.com/rocket-pool/smartnode/rocketpool/api/minipool" "github.com/rocket-pool/smartnode/rocketpool/api/pdao" "github.com/rocket-pool/smartnode/shared/services" - "github.com/rocket-pool/smartnode/shared/services/alerting" - "github.com/rocket-pool/smartnode/shared/services/alerting/alertmanager/models" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" @@ -295,29 +293,6 @@ func getStatus(c *cli.Context) (*api.NodeStatusResponse, error) { return err }) - // Get alerts from Alertmanager - wg.Go(func() error { - alerts, err := alerting.FetchAlerts(cfg) - if err != nil { - // no reason to make `rocketpool node status` fail if we can't get alerts - // (this is more likely to happen in native mode than docker where - // alertmanager is more complex to set up) - // Do save a warning though to print to the user - response.Warning = fmt.Sprintf("Error fetching alerts from Alertmanager: %s", err) - alerts = make([]*models.GettableAlert, 0) - } - response.Alerts = make([]api.NodeAlert, len(alerts)) - - for i, a := range alerts { - response.Alerts[i] = api.NodeAlert{ - State: *a.Status.State, - Labels: a.Labels, - Annotations: a.Annotations, - } - } - return nil - }) - // Wait for data if err := wg.Wait(); err != nil { return nil, err diff --git a/shared/services/alerting/alerting.go b/shared/services/alerting/alerting.go index 7b1bf7333..1affb2220 100644 --- a/shared/services/alerting/alerting.go +++ b/shared/services/alerting/alerting.go @@ -11,6 +11,7 @@ import ( apialert "github.com/rocket-pool/smartnode/shared/services/alerting/alertmanager/client/alert" "github.com/rocket-pool/smartnode/shared/services/alerting/alertmanager/models" "github.com/rocket-pool/smartnode/shared/services/config" + "github.com/rocket-pool/smartnode/shared/types/api" ) const ( @@ -37,6 +38,39 @@ func FetchAlerts(cfg *config.RocketPoolConfig) ([]*models.GettableAlert, error) return resp.Payload, nil } +func FetchNodeAlerts(cfg *config.RocketPoolConfig) ([]api.NodeAlert, error) { + alerts, err := FetchAlerts(cfg) + if err != nil { + return nil, err + } + + response := make([]api.NodeAlert, len(alerts)+1) + + for i, a := range alerts { + response[i] = api.NodeAlert{ + State: *a.Status.State, + Labels: a.Labels, + Annotations: a.Annotations, + } + } + + labels := map[string]string{ + "alertname": "NodeAlert", + "severity": "critical", + } + annotations := map[string]string{ + "summary": "Node Alert", + "description": "Node Alert", + } + response[len(alerts)] = api.NodeAlert{ + State: "active", + Labels: labels, + Annotations: annotations, + } + + return response, nil +} + // Sends an alert when the node automatically changed a node's fee recipient or attempted to (success or failure). // If alerting/metrics are disabled, this function does nothing. func AlertFeeRecipientChanged(cfg *config.RocketPoolConfig, newFeeRecipient common.Address, succeeded bool) error { diff --git a/shared/services/services.go b/shared/services/services.go index 34b25ff00..ec6a92e50 100644 --- a/shared/services/services.go +++ b/shared/services/services.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "os" + "path/filepath" "sync" "github.com/docker/docker/client" @@ -11,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/bindings/utils/eth" + rpSettings "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/beacon" @@ -159,10 +161,17 @@ func GetDocker(c *cli.Context) (*client.Client, error) { func getConfig(c *cli.Context) (*config.RocketPoolConfig, error) { var err error initCfg.Do(func() { - settingsFile := os.ExpandEnv(c.GlobalString("settings")) - cfg, err = rp.LoadConfigFromFile(settingsFile) + settingsFile := c.GlobalString("settings") + if settingsFile == "" { + configDir := c.GlobalString("config-path") + if configDir != "" { + settingsFile = filepath.Join(configDir, rpSettings.SettingsFile) + } + } + expanded := os.ExpandEnv(settingsFile) + cfg, err = rp.LoadConfigFromFile(expanded) if cfg == nil && err == nil { - err = fmt.Errorf("Settings file [%s] not found.", settingsFile) + err = fmt.Errorf("settings file [%s] not found", expanded) } }) return cfg, err