Skip to content

Commit

Permalink
Add NextRunTime as schedule parameter to monitors and ping
Browse files Browse the repository at this point in the history
  • Loading branch information
jdotjdot committed May 29, 2022
1 parent f250476 commit cd6ff9d
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 21 deletions.
49 changes: 35 additions & 14 deletions cmd/discover_logic_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cronitorio/cronitor-cli/lib"
"github.com/manifoldco/promptui"
"os"
"strconv"
"strings"
)

Expand All @@ -34,6 +35,28 @@ func NewWrappedWindowsTask(t taskmaster.RegisteredTask) WrappedWindowsTask {
return w
}

func (r WrappedWindowsTask) FullName() string {
hostname, err := os.Hostname()
if err != nil {
log(fmt.Sprintf("err: %v", err))
hostname = "[no-hostname]"
}
// Windows Task Scheduler won't allow multiple tasks with the same name, so using
// the tasks' name should be safe. You also do not seem to be able to edit the name
// in Windows Task Scheduler, so this seems safe as the Key as well.
fullName := fmt.Sprintf("%s/%s", hostname, r.Name)
// Max name length of 75, so we need to truncate
if len(fullName) >= 74 {
fullName = fullName[:74]
}

return fullName
}

func (r WrappedWindowsTask) WindowsKey() string {
return getWindowsKey(r.FullName())
}

func (r WrappedWindowsTask) IsMicrosoftTask() bool {
return strings.HasPrefix(r.Path, "\\Microsoft\\")
}
Expand All @@ -55,6 +78,14 @@ func (r WrappedWindowsTask) GetCommandToRun() string {
return strings.Join(commands, " && ")
}

func (r WrappedWindowsTask) GetNextRunTime() int64 {
return r.NextRunTime.Unix()
}

func (r WrappedWindowsTask) GetNextRunTimeString() string {
return strconv.Itoa(int(r.GetNextRunTime()))
}

func processWindowsTaskScheduler() bool {
const CronitorWindowsPath = "C:\\Program Files\\cronitor.exe"

Expand All @@ -81,23 +112,12 @@ func processWindowsTaskScheduler() bool {
continue
}

hostname, err := os.Hostname()
if err != nil {
log(fmt.Sprintf("err: %v", err))
}
// Windows Task Scheduler won't allow multiple tasks with the same name, so using
// the tasks' name should be safe. You also do not seem to be able to edit the name
// in Windows Task Scheduler, so this seems safe as the Key as well.
fullName := fmt.Sprintf("%s/%s", hostname, task.Name)
// Max name length of 75, so we need to truncate
if len(fullName) >= 74 {
fullName = fullName[:74]
}
defaultName := fullName
defaultName := t.FullName()
tags := createTags()
key := getWindowsKey(fullName)
key := t.WindowsKey()
name := defaultName
skip := false
schedule := t.GetNextRunTimeString()

// The monitor name will always be the same, so we don't have to fetch it
// from the Cronitor existing monitors
Expand Down Expand Up @@ -149,6 +169,7 @@ func processWindowsTaskScheduler() bool {
Type: "job",
Notify: notifications,
NoStdoutPassthru: noStdoutPassthru,
Schedule: schedule,
}
tz := effectiveTimezoneLocationName()
if tz.Name != "" {
Expand Down
10 changes: 7 additions & 3 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,14 @@ func RunCommand(subcommand string, withEnvironment bool, withMonitoring bool) in

startTime := makeStamp()
series := formatStamp(startTime)
schedule := ""

if withMonitoring {
monitoringWaitGroup.Add(1)
go sendPing("run", monitorCode, subcommand, series, startTime, nil, nil, nil, &monitoringWaitGroup)
if runtime.GOOS == "windows" {
schedule = GetNextRunFromMonitorKey(monitorCode)
}
go sendPing("run", monitorCode, subcommand, series, startTime, nil, nil, nil, schedule, &monitoringWaitGroup)
}

log(fmt.Sprintf("Running subcommand: %s", subcommand))
Expand Down Expand Up @@ -201,7 +205,7 @@ func RunCommand(subcommand string, withEnvironment bool, withMonitoring bool) in
if err == nil {
if withMonitoring {
monitoringWaitGroup.Add(1)
go sendPing("complete", monitorCode, string(outputForPing), series, endTime, &duration, &exitCode, metrics, &monitoringWaitGroup)
go sendPing("complete", monitorCode, string(outputForPing), series, endTime, &duration, &exitCode, metrics, schedule, &monitoringWaitGroup)
monitoringWaitGroup.Add(1)
go shipLogData(tempFile, series, &monitoringWaitGroup)
}
Expand All @@ -221,7 +225,7 @@ func RunCommand(subcommand string, withEnvironment bool, withMonitoring bool) in

if withMonitoring {
monitoringWaitGroup.Add(1)
go sendPing("fail", monitorCode, message, series, endTime, &duration, &exitCode, metrics, &monitoringWaitGroup)
go sendPing("fail", monitorCode, message, series, endTime, &duration, &exitCode, metrics, schedule, &monitoringWaitGroup)
monitoringWaitGroup.Add(1)
go shipLogData(tempFile, series, &monitoringWaitGroup)
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ Example when using authenticated ping requests:
Run: func(cmd *cobra.Command, args []string) {
var wg sync.WaitGroup

uniqueIdentifier := args[0]

wg.Add(1)
go sendPing(getEndpointFromFlag(), args[0], msg, series, makeStamp(), nil, nil, nil, &wg)
var schedule = ""

go sendPing(getEndpointFromFlag(), uniqueIdentifier, msg, series, makeStamp(), nil, nil, nil, schedule, &wg)
wg.Wait()
},
}
Expand Down
11 changes: 8 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func initConfig() {
}
}

func sendPing(endpoint string, uniqueIdentifier string, message string, series string, timestamp float64, duration *float64, exitCode *int, metrics map[string]int, group *sync.WaitGroup) {
func sendPing(endpoint string, uniqueIdentifier string, message string, series string, timestamp float64, duration *float64, exitCode *int, metrics map[string]int, schedule string, group *sync.WaitGroup) {
defer group.Done()

Client := &http.Client{
Expand All @@ -129,6 +129,7 @@ func sendPing(endpoint string, uniqueIdentifier string, message string, series s
formattedDuration := ""
formattedStatusCode := ""
formattedMetrics := ""
formattedSchedule := ""

if timestamp > 0 {
formattedStamp = fmt.Sprintf("&stamp=%s", formatStamp(timestamp))
Expand All @@ -151,6 +152,10 @@ func sendPing(endpoint string, uniqueIdentifier string, message string, series s
formattedDuration = fmt.Sprintf("&duration=%s", formatStamp(*duration))
}

if schedule != "" {
formattedSchedule = fmt.Sprintf("&schedule=%s", schedule)
}

// We aren't using exit code at time of writing, but we have the field available for healthcheck monitors.
if exitCode != nil {
formattedStatusCode = fmt.Sprintf("&status_code=%d", *exitCode)
Expand Down Expand Up @@ -204,10 +209,10 @@ func sendPing(endpoint string, uniqueIdentifier string, message string, series s

if len(authenticationKey) > 0 {
// Authenticated pings when available
uri = fmt.Sprintf("%s/ping/%s/%s?state=%s&try=%d%s%s%s%s%s%s%s%s", pingApiHost, authenticationKey, uniqueIdentifier, endpoint, i, formattedStamp, message, hostname, formattedDuration, series, formattedStatusCode, formattedMetrics, env)
uri = fmt.Sprintf("%s/ping/%s/%s?state=%s&try=%d%s%s%s%s%s%s%s%s%s", pingApiHost, authenticationKey, uniqueIdentifier, endpoint, i, formattedStamp, message, hostname, formattedDuration, series, formattedStatusCode, formattedMetrics, env, formattedSchedule)
} else {
// Fallback to sending an unauthenticated ping
uri = fmt.Sprintf("%s/%s/%s?try=%d%s%s%s%s%s%s%s%s", pingApiHost, uniqueIdentifier, endpoint, i, formattedStamp, message, hostname, formattedDuration, series, formattedStatusCode, formattedMetrics, env)
uri = fmt.Sprintf("%s/%s/%s?try=%d%s%s%s%s%s%s%s%s%s", pingApiHost, uniqueIdentifier, endpoint, i, formattedStamp, message, hostname, formattedDuration, series, formattedStatusCode, formattedMetrics, env, formattedSchedule)
}

log("Sending ping " + uri)
Expand Down
8 changes: 8 additions & 0 deletions cmd/root_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build !windows
// +build !windows

package cmd

func GetNextRunFromMonitorKey(key string) string {
return ""
}
37 changes: 37 additions & 0 deletions cmd/root_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//go:build windows
// +build windows

package cmd

import (
"fmt"
"github.com/capnspacehook/taskmaster"
)

// GetNextRunFromMonitorKey returns the NextRunTime timestamp from Windows
// Task Scheduler. Since each `cronitor ping` call is run independently,
// this call can't be memoized, regardless of how expensive it is.
func GetNextRunFromMonitorKey(key string) string {
taskService, err := taskmaster.Connect()
if err != nil {
log(fmt.Sprintf("err: %v", err))
return ""
}
defer taskService.Disconnect()
collection, err := taskService.GetRegisteredTasks()
if err != nil {
log(fmt.Sprintf("err: %v", err))
return ""
}
defer collection.Release()

for _, task := range collection {
t := NewWrappedWindowsTask(task)

if t.WindowsKey() == key {
return t.GetNextRunTimeString()
}
}

return ""
}

0 comments on commit cd6ff9d

Please sign in to comment.