Skip to content

Commit

Permalink
Merge branch 'FEAT/web-dashboard' of https://github.com/bisohns/saido
Browse files Browse the repository at this point in the history
…into FEAT/web-dashboard
  • Loading branch information
silvareal committed Dec 30, 2022
2 parents f95b218 + 7317336 commit 4bb049a
Show file tree
Hide file tree
Showing 20 changed files with 174 additions and 82 deletions.
83 changes: 55 additions & 28 deletions client/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,61 @@ func (hosts *HostsController) setReadOnlyHost(hostlist config.HostList) {
hosts.ReadOnlyHosts = hostlist
}

func (hosts *HostsController) sendMetric(host config.Host, client *Client) {
func (hosts *HostsController) handleError(err error, metric string, host config.Host, client *Client) {
var errorContent string
if !strings.Contains(fmt.Sprintf("%s", err), "127") {
errorContent = fmt.Sprintf("Could not retrieve metric %s from driver %s with error %s", metric, host.Address, err)
} else {
errorContent = fmt.Sprintf("Command %s not found on driver %s", metric, host.Address)
}
log.Debug(errorContent)
//FIXME: what kind of errors do we especially want to reset driver for
if _, ok := err.(*driver.SSHConnectError); ok {
hosts.resetDriver(host)
}
message := &SendMessage{
Message: ErrorMessage{
Error: errorContent,
Host: host.Address,
Name: metric,
},
Error: true,
}
client.Send <- message
}

func (hosts *HostsController) sendMetric(host config.Host, metrics map[string]string, client *Client) {
var (
err error
data []byte
initializedMetric inspector.Inspector
platformDetails driver.SystemDetails
)
if hosts.getDriver(host.Address) == nil {
hosts.resetDriver(host)
}
for metric, custom := range hosts.Info.Metrics {
for metric, custom := range metrics {
inspectorDriver := hosts.getDriver(host.Address)
initializedMetric, err := inspector.Init(metric, inspectorDriver, custom)
initializedMetric, err = inspector.Init(metric, inspectorDriver, custom)
if err != nil {
log.Error(err)
hosts.handleError(err, metric, host, client)
continue
}
platformDetails, err = (*inspectorDriver).GetDetails()
if err != nil {
log.Error(err)
hosts.handleError(err, metric, host, client)
continue
}
data, err := initializedMetric.Execute()
data, err = initializedMetric.Execute()
if err == nil {
var unmarsh interface{}
json.Unmarshal(data, &unmarsh)
message := &SendMessage{
Message: Message{
Host: host.Address,
Platform: (*inspectorDriver).GetDetails().Name,
Platform: platformDetails.Name,
Name: metric,
Data: unmarsh,
},
Expand All @@ -96,27 +133,7 @@ func (hosts *HostsController) sendMetric(host config.Host, client *Client) {
client.Send <- message
}
} else {
// check for error 127 which means command was not found
var errorContent string
if !strings.Contains(fmt.Sprintf("%s", err), "127") {
errorContent = fmt.Sprintf("Could not retrieve metric %s from driver %s with error %s", metric, host.Address, err)
} else {
errorContent = fmt.Sprintf("Command %s not found on driver %s", metric, host.Address)
}
log.Debug(errorContent)
//FIXME: what kind of errors do we especially want to reset driver for
if _, ok := err.(*driver.SSHConnectError); ok {
hosts.resetDriver(host)
}
message := &SendMessage{
Message: ErrorMessage{
Error: errorContent,
Host: host.Address,
Name: metric,
},
Error: true,
}
client.Send <- message
hosts.handleError(err, metric, host, client)
}
}
}
Expand All @@ -128,7 +145,10 @@ func (hosts *HostsController) Poll(client *Client) {
return
}
if config.Contains(hosts.ReadOnlyHosts, host) {
go hosts.sendMetric(host, client)
// TODO: Decide if we want an override or a merge
// For now we use a merge
metrics := config.MergeMetrics(hosts.Info.Metrics, host.Metrics)
go hosts.sendMetric(host, metrics, client)
}
}
log.Debugf("Delaying for %d seconds", hosts.Info.PollInterval)
Expand Down Expand Up @@ -175,11 +195,18 @@ func (hosts *HostsController) ServeHTTP(w http.ResponseWriter, req *http.Request
// NewHostsController : initialze host controller with config file
func NewHostsController(cfg *config.Config) *HostsController {
dashboardInfo := config.GetDashboardInfoConfig(cfg)
for metric, _ := range dashboardInfo.Metrics {
for metric := range dashboardInfo.Metrics {
if !inspector.Valid(metric) {
log.Fatalf("%s is not a valid metric", metric)
}
}
for _, host := range dashboardInfo.Hosts {
for metric := range host.Metrics {
if !inspector.Valid(metric) {
log.Fatalf("%s is not a valid metric", metric)
}
}
}

hosts := &HostsController{
Info: dashboardInfo,
Expand Down
2 changes: 2 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ hosts:
username: root
password: somethingSecret
port: 2222
metrics:
process:
"192.0.1.4":
# local driver
connection:
Expand Down
50 changes: 38 additions & 12 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ import (
"gopkg.in/yaml.v2"
)

type HostList = []string
type Metrics = map[string]string

type DashboardInfo struct {
Hosts []Host
Metrics map[string]string
Metrics Metrics
Title string
PollInterval int
}

type HostList = []string

func Contains(hostList HostList, host Host) bool {
for _, compare := range hostList {
if host.Address == compare {
Expand All @@ -30,6 +31,17 @@ func Contains(hostList HostList, host Host) bool {
return false
}

func MergeMetrics(a, b Metrics) (metrics Metrics) {
metrics = Metrics{}
inputs := [2]Metrics{a, b}
for _, metric := range inputs {
for k, v := range metric {
metrics[k] = v
}
}
return
}

// GetAllHostAddresses : returns list of all hosts in the dashboard
func (dashboardInfo *DashboardInfo) GetAllHostAddresses() (addresses HostList) {
addresses = []string{}
Expand All @@ -53,6 +65,8 @@ type Host struct {
Address string
Alias string
Connection *Connection
// Metrics : extend global metrics with single metrics
Metrics Metrics
}

type Config struct {
Expand All @@ -75,22 +89,25 @@ func LoadConfig(configPath string) *Config {
return config
}

func coerceMetrics(rawMetrics map[interface{}]interface{}) map[string]string {
metrics := make(map[string]string)
for metric, customCommand := range rawMetrics {
metric := fmt.Sprintf("%v", metric)
metrics[metric] = fmt.Sprintf("%v", customCommand)
}
return metrics
}

func GetDashboardInfoConfig(config *Config) *DashboardInfo {
dashboardInfo := &DashboardInfo{
Title: "Saido",
}
if config.Title != "" {
dashboardInfo.Title = config.Title
}
metrics := make(map[string]string)

dashboardInfo.Hosts = parseConfig("root", "", config.Hosts, &Connection{})
for metric, customCommand := range config.Metrics {
metric := fmt.Sprintf("%v", metric)
metrics[metric] = fmt.Sprintf("%v", customCommand)

}
dashboardInfo.Metrics = metrics
dashboardInfo.Metrics = coerceMetrics(config.Metrics)
for _, host := range dashboardInfo.Hosts {
log.Debugf("%s: %v", host.Address, host.Connection)
}
Expand All @@ -116,12 +133,12 @@ func parseConnection(conn map[interface{}]interface{}) *Connection {
func parseConfig(name string, host string, group map[interface{}]interface{}, currentConnection *Connection) []Host {
currentConn := currentConnection
allHosts := []Host{}
log.Infof("Loading config for %s and host: %s with Connection: %+v", name, host, currentConn)
log.Debugf("Loading config for %s and host: %s with Connection: %+v", name, host, currentConn)
isParent := false // Set to true for groups that contain just children data i.e children
if conn, ok := group["connection"]; ok {
v, ok := conn.(map[interface{}]interface{})
if !ok {
log.Errorf("Failed to parse connection for %s", name)
log.Fatalf("Failed to parse connection for %s", name)
}

currentConn = parseConnection(v)
Expand All @@ -147,13 +164,22 @@ func parseConfig(name string, host string, group map[interface{}]interface{}, cu

if !isParent {
currentConn.Host = host

newHost := Host{
Address: host,
Connection: currentConn,
}
if alias, ok := group["alias"]; ok {
newHost.Alias = alias.(string)
}
if metrics, ok := group["metrics"]; ok {
rawMetrics, ok := metrics.(map[interface{}]interface{})
if !ok {
log.Fatalf("Failed to parse metrics for %s", name)
}
individualMetrics := coerceMetrics(rawMetrics)
newHost.Metrics = individualMetrics
}

allHosts = append(allHosts, newHost)
}
Expand Down
2 changes: 1 addition & 1 deletion driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type Driver interface {
ReadFile(path string) (string, error)
RunCommand(command string) (string, error)
// shows the driver details, not sure if we should be showing OS name
GetDetails() SystemDetails
GetDetails() (SystemDetails, error)
}

func ToDriver(conn config.Connection) Driver {
Expand Down
9 changes: 6 additions & 3 deletions driver/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ func (d *Local) RunCommand(command string) (string, error) {
var cmd *exec.Cmd
log.Debugf("Running command `%s` ", command)
if d.Info == nil {
d.GetDetails()
_, err := d.GetDetails()
if err != nil {
return ``, err
}
}
if d.Info.IsLinux || d.Info.IsDarwin {
cmd = exec.Command("bash", "-c", command)
Expand All @@ -58,7 +61,7 @@ func (d *Local) RunCommand(command string) (string, error) {
return string(out), nil
}

func (d *Local) GetDetails() SystemDetails {
func (d *Local) GetDetails() (SystemDetails, error) {
if d.Info == nil {
details := &SystemDetails{}
details.Name = strings.Title(runtime.GOOS)
Expand All @@ -73,5 +76,5 @@ func (d *Local) GetDetails() SystemDetails {
details.Extra = runtime.GOARCH
d.Info = details
}
return *d.Info
return *d.Info, nil
}
5 changes: 3 additions & 2 deletions driver/local_unix_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows

package driver
Expand All @@ -17,8 +18,8 @@ func TestUnixLocalRunCommand(t *testing.T) {

func TestUnixLocalSystemDetails(t *testing.T) {
d := Local{}
details := d.GetDetails()
if !(details.IsLinux || details.IsDarwin) {
details, err := d.GetDetails()
if err != nil || !(details.IsLinux || details.IsDarwin) {
t.Errorf("Expected Darwin or Linux on unix test, got %s", details.Name)
}
}
4 changes: 2 additions & 2 deletions driver/local_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ func TestWindowsRunCommand(t *testing.T) {

func TestWindowsLocalSystemDetails(t *testing.T) {
d := Local{}
details := d.GetDetails()
if !details.IsWindows {
details, err := d.GetDetails()
if err != nil || !details.IsWindows {
t.Errorf("Expected windows got %s", details.Name)
}
}
7 changes: 4 additions & 3 deletions driver/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (d *SSH) RunCommand(command string) (string, error) {
return string(out), nil
}

func (d *SSH) GetDetails() SystemDetails {
func (d *SSH) GetDetails() (SystemDetails, error) {
if d.Info == nil {
// TODO: Check for goph specific errors
// within RunCommand and only return errors that are not
Expand All @@ -145,7 +145,8 @@ func (d *SSH) GetDetails() SystemDetails {
}
} else {
//FIXME: Fix issue with establishing connection on SSH
panic(fmt.Sprintf("Could not find platform details for %s: %s", d.Host, err))
log.Errorf("Could not find platform details for %s: %s", d.Host, err)
return SystemDetails{}, err
}
}
details := &SystemDetails{}
Expand All @@ -160,5 +161,5 @@ func (d *SSH) GetDetails() SystemDetails {
}
d.Info = details
}
return *d.Info
return *d.Info, nil
}
4 changes: 2 additions & 2 deletions driver/ssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ func TestSSHSystemDetails(t *testing.T) {
return
}
d := NewSSHForTest()
details := d.GetDetails()
if !details.IsLinux {
details, err := d.GetDetails()
if err != nil || !details.IsLinux {
t.Errorf("Expected linux server for ssh test got %#v", details)
}
}
4 changes: 2 additions & 2 deletions driver/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (d *Web) RunCommand(command string) (string, error) {
return ``, errors.New("Cannot read file on web driver")
}

func (d *Web) GetDetails() SystemDetails {
func (d *Web) GetDetails() (SystemDetails, error) {
if d.Info == nil {
details := &SystemDetails{
Name: "web",
Expand All @@ -71,5 +71,5 @@ func (d *Web) GetDetails() SystemDetails {
}
d.Info = details
}
return *d.Info
return *d.Info, nil
}
4 changes: 2 additions & 2 deletions driver/web_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func TestWebRunCommand(t *testing.T) {

func TestWebSystemDetails(t *testing.T) {
d := NewWebForTest()
details := d.GetDetails()
if !details.IsWeb {
details, err := d.GetDetails()
if err != nil || !details.IsWeb {
t.Errorf("Expected web driver for web test got %s", details.Name)
}
}
Loading

0 comments on commit 4bb049a

Please sign in to comment.