Skip to content

Commit

Permalink
Merge pull request #108 from mfreeman451/99-feat-sparkgraphs-in-grid-…
Browse files Browse the repository at this point in the history
…mode

99 feat sparkgraphs in grid mode
  • Loading branch information
mfreeman451 authored Jan 25, 2025
2 parents 9d09504 + 766ff6a commit 3c4ff12
Show file tree
Hide file tree
Showing 27 changed files with 1,128 additions and 477 deletions.
47 changes: 28 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,45 +30,45 @@ ServiceRadar can be installed via direct downloads from GitHub releases.
Install these components on your monitored host:
```bash
# Download and install core components
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-agent_1.0.5.deb \
-O https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-poller_1.0.5.deb
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-agent_1.0.6.deb \
-O https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-poller_1.0.6.deb

sudo dpkg -i serviceradar-agent_1.0.5.deb serviceradar-poller_1.0.5.deb
sudo dpkg -i serviceradar-agent_1.0.6.deb serviceradar-poller_1.0.6.deb
```

On a separate machine (recommended) or the same host:
```bash
# Download and install cloud service
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-cloud_1.0.5.deb
sudo dpkg -i serviceradar-cloud_1.0.5.deb
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-cloud_1.0.6.deb
sudo dpkg -i serviceradar-cloud_1.0.6.deb
```

#### Optional: Dusk Node Monitoring
If you're running a [Dusk](https://dusk.network/) node and want specialized monitoring:
```bash
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.5/serviceradar-dusk-checker_1.0.5.deb
sudo dpkg -i serviceradar-dusk-checker_1.0.5.deb
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.6/serviceradar-dusk-checker_1.0.6.deb
sudo dpkg -i serviceradar-dusk-checker_1.0.6.deb
```

#### Distributed Setup
For larger deployments where components run on different hosts:

1. On monitored hosts:
```bash
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.5/serviceradar-agent_1.0.5.deb
sudo dpkg -i serviceradar-agent_1.0.5.deb
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.6/serviceradar-agent_1.0.6.deb
sudo dpkg -i serviceradar-agent_1.0.6.deb
```

2. On monitoring host:
```bash
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-poller_1.0.5.deb
sudo dpkg -i serviceradar-poller_1.0.5.deb
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-poller_1.0.6.deb
sudo dpkg -i serviceradar-poller_1.0.6.deb
```

3. On cloud host:
```bash
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-cloud_1.0.5.deb
sudo dpkg -i serviceradar-cloud_1.0.5.deb
curl -LO https://github.com/mfreeman451/serviceradar/releases/download/1.0.3/serviceradar-cloud_1.0.6.deb
sudo dpkg -i serviceradar-cloud_1.0.6.deb
```

## Architecture
Expand Down Expand Up @@ -170,19 +170,19 @@ cd serviceradar

1. **Agent Installation** (on monitored hosts):
```bash
sudo dpkg -i serviceradar-dusk-checker_1.0.5.deb # For Dusk nodes
sudo dpkg -i serviceradar-dusk-checker_1.0.6.deb # For Dusk nodes
# or
sudo dpkg -i serviceradar-agent_1.0.5.deb # For other hosts
sudo dpkg -i serviceradar-agent_1.0.6.deb # For other hosts
```

2. **Poller Installation** (on any host in your network):
```bash
sudo dpkg -i serviceradar-poller_1.0.5.deb
sudo dpkg -i serviceradar-poller_1.0.6.deb
```

3. **Cloud Installation** (on a reliable host):
```bash
sudo dpkg -i serviceradar-cloud_1.0.5.deb
sudo dpkg -i serviceradar-cloud_1.0.6.deb
```

## Configuration
Expand Down Expand Up @@ -258,13 +258,17 @@ Default location: `/etc/serviceradar/poller.json`
{
"service_type": "port",
"service_name": "SSH",
"port": 22
"details": "127.0.0.1:22"
},
{
"service_type": "grpc",
"service_name": "dusk",
"details": "127.0.0.1:50052"
},
{
"service_type": "icmp",
"service_name": "ping"
},
{
"service_type": "sweep",
"service_name": "network_sweep",
Expand All @@ -276,7 +280,7 @@ Default location: `/etc/serviceradar/poller.json`
"cloud_address": "changeme:50052",
"listen_addr": ":50053",
"poll_interval": "30s",
"poller_id": "home-poller-1",
"poller_id": "dusk",
"service_name": "PollerService",
"service_type": "grpc"
}
Expand All @@ -292,6 +296,11 @@ Default location: `/etc/serviceradar/cloud.json`
"grpc_addr": ":50052",
"alert_threshold": "5m",
"known_pollers": ["home-poller-1"],
"metrics": {
"enabled": true,
"retention": 100,
"max_nodes": 10000
},
"webhooks": [
{
"enabled": false,
Expand Down
2 changes: 1 addition & 1 deletion buildAll.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

VERSION=${VERSION:-1.0.5}
VERSION=${VERSION:-1.0.6}


./setup-deb-poller.sh
Expand Down
4 changes: 3 additions & 1 deletion cmd/cloud/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func run() error {
return err
}

log.Printf("cfg: %v", cfg)

// Create root context for lifecycle management
ctx := context.Background()

Expand All @@ -39,7 +41,7 @@ func run() error {
}

// Create and configure API server
apiServer := api.NewAPIServer()
apiServer := api.NewAPIServer(server.GetMetricsManager())
server.SetAPIServer(apiServer)

// Start HTTP API server in background
Expand Down
47 changes: 43 additions & 4 deletions pkg/agent/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import (
"net"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
)

const (
partsForPortDetails = 2
)

var (
Expand Down Expand Up @@ -55,21 +61,54 @@ type PortChecker struct {
Port int
}

func NewPortChecker(details string) (*PortChecker, error) {
if details == "" {
return nil, errDetailsRequired
}

// Split the details into host and port
parts := strings.Split(details, ":")
if len(parts) != partsForPortDetails {
return nil, errInvalidDetailsFormat
}

host := parts[0]

port, err := strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("%w: %d", errInvalidPort, port)
}

if port <= 0 || port > 65535 {
return nil, fmt.Errorf("%w: %d", errInvalidPort, port)
}

return &PortChecker{
Host: host,
Port: port,
}, nil
}

// Check validates if a port is accessible.
func (p *PortChecker) Check(ctx context.Context) (isAccessible bool, statusMsg string) {
var d net.Dialer

addr := fmt.Sprintf("%s:%d", p.Host, p.Port)

start := time.Now()

conn, err := d.DialContext(ctx, "tcp", addr)
if err != nil {
return false, fmt.Sprintf("Port %d is not accessible: %v", p.Port, err)
return false, fmt.Sprintf(`{"error": "Port %d is not accessible: %v"}`, p.Port, err)
}

responseTime := time.Since(start).Nanoseconds()

if err = conn.Close(); err != nil {
log.Printf("Error closing connection: %v", err)

return false, "Error closing connection"
return false, `{"error": "Error closing connection"}`
}

return true, fmt.Sprintf("Port %d is accessible", p.Port)
// Return raw data
return true, fmt.Sprintf(`{"host": "%q", "port": %d, "response_time": %d}`, p.Host, p.Port, responseTime)
}
8 changes: 4 additions & 4 deletions pkg/agent/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package agent
import "errors"

var (
errGrpcAddressRequired = errors.New("address is required for gRPC checker")
errUnknownCheckerType = errors.New("unknown checker type")
errGrpcMissingConfig = errors.New("no configuration or address provided for gRPC checker")
errShutdown = errors.New("error while shutting down")
errShutdown = errors.New("error while shutting down")
errInvalidPort = errors.New("invalid port")
errDetailsRequired = errors.New("details field is required for port checks")
errInvalidDetailsFormat = errors.New("invalid details format: expected 'host:port'")
)
21 changes: 15 additions & 6 deletions pkg/agent/external_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package agent

import (
"context"
"encoding/json"
"fmt"
"log"
"time"

grpcpkg "github.com/mfreeman451/serviceradar/pkg/grpc"
"github.com/mfreeman451/serviceradar/proto"
Expand Down Expand Up @@ -76,31 +78,38 @@ func (e *ExternalChecker) Check(ctx context.Context) (isAccessible bool, statusM
healthy, err := e.client.CheckHealth(ctx, "")
if err != nil {
log.Printf("External checker %s: Health check failed: %v", e.serviceName, err)

return false, fmt.Sprintf("Failed to check %s: %v", e.serviceName, err)
return false, fmt.Sprintf("Health check failed: %v", err)
}

if !healthy {
log.Printf("External checker %s: Service reported unhealthy", e.serviceName)

return false, fmt.Sprintf("%s is not healthy", e.serviceName)
return false, "Service reported unhealthy"
}

// Then get block details through AgentService
client := proto.NewAgentServiceClient(e.client.GetConnection())

start := time.Now()
status, err := client.GetStatus(ctx, &proto.StatusRequest{
ServiceName: e.serviceName,
ServiceType: e.serviceType,
})

if err != nil {
log.Printf("External checker %s: Failed to get details: %v", e.serviceName, err)

return true, fmt.Sprintf("%s is healthy but details unavailable", e.serviceName)
return true, "Service healthy but details unavailable"
}

log.Printf("External checker %s: Received status message: %s", e.serviceName, status.Message)
responseTime := time.Since(start).Nanoseconds()

// Parse status.Message into response structure
var details map[string]interface{}
if err := json.Unmarshal([]byte(status.Message), &details); err != nil {
return true, fmt.Sprintf(`{"response_time": %d, "error": "invalid details format"}`, responseTime)
}

// Pass the details directly in the status message
return true, status.Message
}

Expand Down
58 changes: 58 additions & 0 deletions pkg/agent/icmp_checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Package agent pkg/agent/icmp_checker.go
package agent

import (
"context"
"fmt"
"time"

"github.com/mfreeman451/serviceradar/pkg/models"
"github.com/mfreeman451/serviceradar/pkg/scan"
)

type ICMPChecker struct {
Host string
Count int
}

const (
defaultICMPCount = 3
defaultConcurrent = 1
)

func (p *ICMPChecker) Check(ctx context.Context) (isDown bool, results string) {
scanner := scan.NewCombinedScanner(defaultICMPCount*time.Second, defaultConcurrent, p.Count)

target := models.Target{
Host: p.Host,
Mode: models.ModeICMP,
}

resultChan, err := scanner.Scan(ctx, []models.Target{target})
if err != nil {
return false, fmt.Sprintf("ICMP check failed: %v", err)
}

var totalResponseTime time.Duration

var successfulPings int

for result := range resultChan {
if result.Error != nil {
return false, result.Error.Error()
}

if result.Available {
totalResponseTime += result.RespTime
successfulPings++
}
}

if successfulPings == 0 {
return false, "No successful ICMP replies"
}

avgResponseTime := totalResponseTime / time.Duration(successfulPings)

return true, fmt.Sprintf("%d", avgResponseTime)
}
Loading

0 comments on commit 3c4ff12

Please sign in to comment.