Skip to content

Commit

Permalink
WIP refactoring -- poller
Browse files Browse the repository at this point in the history
  • Loading branch information
mfreeman451 committed Jan 22, 2025
1 parent b84bce2 commit 9678562
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 302 deletions.
53 changes: 13 additions & 40 deletions cmd/poller/main.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// cmd/poller/main.go
package main

import (
"context"
"flag"
"log"
"os"
"os/signal"
"syscall"

"github.com/mfreeman451/serviceradar/pkg/config"
"github.com/mfreeman451/serviceradar/pkg/lifecycle"
"github.com/mfreeman451/serviceradar/pkg/poller"
)

Expand All @@ -23,49 +21,24 @@ func run() error {
configPath := flag.String("config", "/etc/serviceradar/poller.json", "Path to poller config file")
flag.Parse()

// Create a cancellable context for shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Load configuration
cfg, err := poller.LoadConfig(*configPath)
if err != nil {
// Load and validate configuration using shared config package
var cfg poller.Config
if err := config.LoadAndValidate(*configPath, &cfg); err != nil {
return err
}

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

// Create poller instance
p, err := poller.New(ctx, cfg)
if err != nil {
return err
}
defer p.Close()

// Handle shutdown signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

// Start poller in background
errChan := make(chan error, 1)
go func() {
defer close(errChan)
if err := p.Start(ctx); err != nil {
errChan <- err
}
}()

// Wait for shutdown signal or error
select {
case sig := <-sigChan:
log.Printf("Received signal %v, initiating shutdown", sig)
case err := <-errChan:
if err != nil {
log.Printf("Poller error: %v", err)
return err
}
}

// Initiate graceful shutdown
cancel()

return nil
// Run poller with lifecycle management
return lifecycle.RunServer(ctx, lifecycle.ServerOptions{
Service: p,
EnableHealthCheck: false,
})
}
8 changes: 7 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
"time"
)

var (
errInvalidDuration = fmt.Errorf("invalid duration")
)

// Duration is a wrapper around time.Duration that implements JSON marshaling/unmarshaling.
type Duration struct {
time.Duration
Expand All @@ -26,13 +30,15 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
return nil
case string:
var err error

d.Duration, err = time.ParseDuration(value)
if err != nil {
return fmt.Errorf("invalid duration: %w", err)
}

return nil
default:
return fmt.Errorf("invalid duration type: %T", v)
return errInvalidDuration
}
}

Expand Down
68 changes: 16 additions & 52 deletions pkg/poller/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Package poller pkg/poller/config.go
// pkg/poller/config.go

package poller

import (
"encoding/json"
"fmt"
"os"
"time"

"github.com/mfreeman451/serviceradar/pkg/config"
)

// AgentConfig represents configuration for a single agent.
Expand All @@ -14,34 +15,24 @@ type AgentConfig struct {
Checks []Check `json:"checks"`
}

// Check represents a service check configuration.
type Check struct {
Type string `json:"service_type"`
Name string `json:"service_name"`
Details string `json:"details,omitempty"`
Port int32 `json:"port,omitempty"`
}

// Config represents poller configuration

Check failure on line 26 in pkg/poller/config.go

View workflow job for this annotation

GitHub Actions / lint

Comment should end in a period (godot)
type Config struct {
Agents map[string]AgentConfig `json:"agents"`
CloudAddress string `json:"cloud_address"`
PollInterval Duration `json:"poll_interval"`
PollInterval config.Duration `json:"poll_interval"`
PollerID string `json:"poller_id"`
}

// LoadConfig loads configuration from a file
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}

var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("failed to parse config: %w", err)
}

if err := cfg.validate(); err != nil {
return nil, fmt.Errorf("invalid config: %w", err)
}

return &cfg, nil
}

func (c *Config) validate() error {
// Validate implements config.Validator interface

Check failure on line 34 in pkg/poller/config.go

View workflow job for this annotation

GitHub Actions / lint

Comment should end in a period (godot)
func (c *Config) Validate() error {
if c.CloudAddress == "" {
return fmt.Errorf("cloud_address is required")
}
Expand All @@ -52,34 +43,7 @@ func (c *Config) validate() error {
return fmt.Errorf("at least one agent configuration is required")
}
if c.PollInterval.Duration == 0 {
c.PollInterval = Duration{Duration: 30 * time.Second} // default
c.PollInterval = config.Duration{Duration: 30 * time.Second} // default
}
return nil
}

// Duration wraps time.Duration for JSON unmarshaling
type Duration struct {
time.Duration
}

func (d *Duration) UnmarshalJSON(b []byte) error {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}

switch value := v.(type) {
case float64:
d.Duration = time.Duration(value)
return nil
case string:
var err error
d.Duration, err = time.ParseDuration(value)
if err != nil {
return fmt.Errorf("invalid duration: %w", err)
}
return nil
default:
return fmt.Errorf("invalid duration type: %T", v)
}
}
Loading

0 comments on commit 9678562

Please sign in to comment.