Writing Your Own Checks
The checkers CLI is designed to be easily extensible. This guide will show you how to write your own checks in Go that fit your organization’s needs.
Check Structure
A check is a Go function that:
- Takes a
types.CheckItem
parameter containing:Name
: Name of the checkDescription
: Optional descriptionType
: Type of the checkCommand
: Optional command for command-based checksParameters
: Map of string parameters passed to the check
- Returns a
types.CheckResult
and an error, whereCheckResult
contains:Name
: Name of the checkType
: Type of the checkStatus
: One ofSuccess
,Failure
,Warning
, orError
Output
: Human-readable output messageError
: Optional error message when Status is Error
- Is registered with the checks registry using
checks.Register
Example Project
-
Create a new directory for your checks project:
mkdir orgchecks cd orgchecks
-
Initialize a new Go module:
go mod init orgchecks
-
Add the checkers library as a dependency:
go get github.com/seastar-consulting/checkers
-
Create a directory for your checks:
mkdir -p checks/access
-
Create a new file
checks/access/api.go
with the example check:package access import ( "encoding/base64" "fmt" "net/http" "os" "github.com/joho/godotenv" "github.com/seastar-consulting/checkers/checks" "github.com/seastar-consulting/checkers/types" ) func init() { // Register your check with a unique name and description checks.Register("access.api_access", "Verify API access is authorized", CheckAPIAccess) } // CheckAPIAccess verifies that access to an API endpoint is authorized func CheckAPIAccess(item types.CheckItem) (types.CheckResult, error) { // Get parameters from the config url, ok := item.Parameters["url"] if !ok || url == "" { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Error, Error: "url parameter is required", }, nil } // Load credentials from .env file if err := godotenv.Load(); err != nil { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Error, Error: "failed to load .env file", }, nil } username := os.Getenv("API_USERNAME") password := os.Getenv("API_PASSWORD") if username == "" || password == "" { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Error, Error: "API_USERNAME and API_PASSWORD must be set in .env file", }, nil } // Create request with Basic Auth req, err := http.NewRequest("GET", url, nil) if err != nil { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Error, Error: fmt.Sprintf("failed to create request: %v", err), }, nil } // Add Basic Auth header auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) req.Header.Add("Authorization", "Basic " + auth) // Perform the check client := &http.Client{} resp, err := client.Do(req) if err != nil { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Error, Error: fmt.Sprintf("Failed to connect to %s: %v", url, err), }, nil } defer resp.Body.Close() if resp.StatusCode == http.StatusUnauthorized { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Failure, Output: "API access denied: invalid credentials", }, nil } if resp.StatusCode != http.StatusOK { return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Failure, Output: fmt.Sprintf("API returned unexpected status code: %d", resp.StatusCode), }, nil } return types.CheckResult{ Name: item.Name, Type: item.Type, Status: types.Success, Output: fmt.Sprintf("Successfully authenticated to API at %s", url), }, nil }
-
Create a
main.go
file in the root directory:package main import ( "fmt" "os" _ "orgchecks/checks/access" "github.com/seastar-consulting/checkers/cmd" ) func main() { if err := cmd.Execute(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } }
-
Create a
.env
file with your API credentials:API_USERNAME=your-username API_PASSWORD=your-password
-
Create a
checks.yaml
file to define your checks:checks: - name: verify-api-access type: access.api_access parameters: url: "https://api.example.com/auth"
-
Run your checks:
go run main.go
If you need to include checks from the standard library, you can use the github.com/seastar-consulting/checkers/checks/all
import to import all the available checks or pick specific packages like github.com/seastar-consulting/checkers/checks/cloud
or github.com/seastar-consulting/checkers/checks/k8s
. Icluding only specific packages helps keep the resulting binary small and focused on your needs.
Check Guidelines
-
Naming Convention:
- Use a namespace prefix for your checks (e.g.,
tools.
,access.
) - Name your checks in CamelCase starting with the prefix Check
- Register the checks in snake_case
- Example:
custom.http_endpoint
,security.ssl_cert_expiry
- Use a namespace prefix for your checks (e.g.,
-
Parameter Handling:
- Always validate required parameters
- Provide sensible defaults for optional parameters
- Document all parameters in comments
- Remember that all parameters are strings in the
Parameters
map
-
Error Handling:
- Return errors for configuration/setup issues
- Use appropriate status in CheckResult:
types.Success
: Check passedtypes.Failure
: Check failed but executed correctlytypes.Warning
: Check passed with warningstypes.Error
: Check couldn’t be executed properly
- Provide clear, actionable error messages
-
Result Format:
- Always set all required fields in CheckResult:
Name
: From the input CheckItemType
: From the input CheckItemStatus
: Success, Failure, Warning, or ErrorOutput
: A descriptive message
- Set
Error
field when Status is Error
- Always set all required fields in CheckResult:
Best Practices
-
Modularity:
- Keep checks focused on a single responsibility
- Break complex checks into smaller, reusable functions
- Use interfaces for external dependencies to enable testing
-
Documentation:
- Add clear comments explaining the check’s purpose
- Document all parameters and their types
- Include example usage in YAML format
- Document any assumptions or limitations
-
Performance:
- Set appropriate timeouts
- Clean up resources (close connections, files)