diff --git a/.gitignore b/.gitignore index 2ee8f80..df76dd7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ examples/mock/mock examples/logging_demo otto/otto ottoctl/ottoctl +cmd/ottoctl/ottoctl/ottoctl mock/mock gpio/gpio station/station diff --git a/client/client.go b/client/client.go index 77680af..a3ab52c 100644 --- a/client/client.go +++ b/client/client.go @@ -118,6 +118,18 @@ func (c *Client) GetLogConfig() (result utils.LogConfig, err error) { return result, nil } +// GetTimers retrieves timer information from the Otto server. +// Returns a slice of TickerInfo containing details about active timers. +// +// This calls the /api/timers endpoint on the server. +func (c *Client) GetTimers() ([]utils.TickerInfo, error) { + var timers []utils.TickerInfo + if err := c.get("/api/timers", &timers); err != nil { + return nil, err + } + return timers, nil +} + // Ping checks if the Otto server is reachable and responding. // Returns nil if the server is healthy, error otherwise. func (c *Client) Ping() error { diff --git a/cmd/ottoctl/cmd_root.go b/cmd/ottoctl/cmd_root.go index 80bb053..27c5145 100644 --- a/cmd/ottoctl/cmd_root.go +++ b/cmd/ottoctl/cmd_root.go @@ -38,6 +38,7 @@ func init() { rootCmd.AddCommand(shutdownCmd) rootCmd.AddCommand(stationsCmd) rootCmd.AddCommand(statsCmd) + rootCmd.AddCommand(timersCmd) rootCmd.AddCommand(versionCmd) } diff --git a/cmd/ottoctl/cmd_timers.go b/cmd/ottoctl/cmd_timers.go new file mode 100644 index 0000000..8682db1 --- /dev/null +++ b/cmd/ottoctl/cmd_timers.go @@ -0,0 +1,42 @@ +package ottoctl + +import ( + "errors" + "fmt" + "log/slog" + "time" + + "github.com/spf13/cobra" +) + +var timersCmd = &cobra.Command{ + Use: "timers", + Short: "Display timer information", + Long: `Display timer information from local or remote Otto instance`, + RunE: timersRun, +} + +func timersRun(cmd *cobra.Command, args []string) error { + // Check if we should connect to a remote server + client := getClient() + if client == nil { + return errors.New("timersRun failed to get a client") + } + + // Remote mode: fetch timers from server + slog.Debug("Fetching timers from remote server", "url", client.BaseURL) + timers, err := client.GetTimers() + if err != nil { + fmt.Fprintf(errOutput, "Error fetching remote timers: %v\n", err) + return err + } + + fmt.Fprintf(cmdOutput, "%20s %7s %6s %s\n", "ticker", "active", "ticks", "last") + fmt.Fprintln(cmdOutput, "--------------------------------------------------------------") + for _, t := range timers { + last := time.Since(t.LastTick) + last = last.Round(time.Second) + fmt.Fprintf(cmdOutput, "%20s %7t %6d %s\n", t.Name, t.Active, t.Ticks, last) + } + return nil +} diff --git a/cmd/ottoctl/cmd_timers_test.go b/cmd/ottoctl/cmd_timers_test.go new file mode 100644 index 0000000..fb17831 --- /dev/null +++ b/cmd/ottoctl/cmd_timers_test.go @@ -0,0 +1,39 @@ +package ottoctl + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTimers(t *testing.T) { + tstfile := "testdata/timers.json" + response, err := os.ReadFile(tstfile) + assert.NoError(t, err, "failed to open") + + tst := newTstInput(timersRun, string(response)) + err = httpQuery(t, tst) + assert.NoError(t, err) + + output := tst.buffer.String() + assert.Contains(t, output, "heartbeat") + assert.Contains(t, output, "sensor_poll") +} + +func TestTimersEmpty(t *testing.T) { + response := "" + tst := newTstInput(timersRun, response) + err := httpQuery(t, tst) + assert.Error(t, err) +} + +func TestTimersError(t *testing.T) { + tst := newTstInput(timersRun, "") + tst.statusCode = 500 + err := httpQuery(t, tst) + assert.Error(t, err) + + output := tst.errbuf.String() + assert.Contains(t, output, "500 - Internal Server Error") +} diff --git a/cmd/ottoctl/ottoctl/Makefile b/cmd/ottoctl/ottoctl/Makefile index b397a57..76e24e7 100644 --- a/cmd/ottoctl/ottoctl/Makefile +++ b/cmd/ottoctl/ottoctl/Makefile @@ -1,2 +1,2 @@ ottoctl: main.go - go build -v . -o ottoctl + go build -v -o ottoctl . diff --git a/cmd/ottoctl/testdata/timers.json b/cmd/ottoctl/testdata/timers.json new file mode 100644 index 0000000..7a56f04 --- /dev/null +++ b/cmd/ottoctl/testdata/timers.json @@ -0,0 +1,20 @@ +[ + { + "name": "heartbeat", + "last_tick": "2026-01-04T16:40:00Z", + "ticks": 120, + "active": true + }, + { + "name": "sensor_poll", + "last_tick": "2026-01-04T16:39:55Z", + "ticks": 240, + "active": true + }, + { + "name": "cleanup", + "last_tick": "2026-01-04T16:35:00Z", + "ticks": 6, + "active": false + } +]