sentinel

package module
v0.0.0-...-9210aab Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 21, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

README.md for go-sentinel

go-sentinel: A Go-based Process & State Manager

go-sentinel is a lightweight, Go-based daemon designed to manage long-running processes (referred to as "states") and execute scheduled checks based on system conditions or time. It provides robust state management, graceful process lifecycle control, and dynamic configuration reloading, making it ideal for maintaining the desired operational state of your system.

✨ Features

  • State Management: Define and transition between different operational states, each launching a specified long-running process with customizable handlers.
  • Check Scheduling: Execute one-shot or periodic checks.
  • Conditional Check Execution: Run checks only when the system is in a specific defined state.
  • Extensible Handlers: Easily integrate new types of checks or states (e.g., mount checks, process restarts, custom cmd wrappers).
  • Robust Logging: Provides detailed logging using zerolog for easy debugging and monitoring.

🚀 Getting Started

Prerequisites

  • Go (1.24 or higher recommended)

Installation

```bash
go get github.com/Lunal98/go-sentinel
```

Basic Usage

go-sentinel is designed to be integrated directly into your Go applications. Here's a basic example:

package main

import (
	"context"

	"os"
	"os/signal"
	"syscall"

	"github.com/Lunal98/go-sentinel"
	"github.com/rs/zerolog"                     // For log level configuration
)

func main() {

	// Optional: Set a specific configuration file path
	// sentinel.SetConfigFile("/etc/my-app/go-sentinel.yaml")

	
	sentinel.SetLogLevel(zerolog.ErrorLevel)

	// Initialize go-sentinel. This loads the configuration and sets up the logger.
	if err := sentinel.Init(); err != nil {
		log.Fatal().Err(err).Msg("Failed to initialize sentinel config")
	}

	// Register custom state and check handlers *before* starting sentinel.
	// Replace 'myCustomCheckHandler' and 'myCustomStateHandler' with your actual implementations.
	// You would typically define these structs and their methods elsewhere in your application.
	sentinel.RegisterCheckHandler("mycustomcheckhandler", &myCustomCheckHandler{})
	sentinel.RegisterStateHandler("mycustomstatehandler", &myCustomStateHandler{})

  sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	
	go sentinel.Start(ctx)
		for {
		select {
		case s := <-sigChan:
			switch s {
			case syscall.SIGHUP:
				log.Info().Msg("SIGHUP received, rotating to the next state")
				sentinel.Next()
			case syscall.SIGINT, syscall.SIGTERM:
				log.Info().Msg("Termination signal received, shutting down")
				return
			}
		case <-ctx.Done():
			log.Info().Msg("Context cancelled, shutting down")
			return
		}
	}

}

Configuration

go-sentinel uses a YAML configuration file (e.g., config.yaml). It searches for this file in the following locations, in order:

  1. /etc/go-sentinel/
  2. $HOME/.go-sentinel/
  3. The current working directory

You specify the path of the config path using the GO_SENTINEL_CONFIG environment variable. Environment variables prefixed with GO_SENTINEL_ (e.g., GO_SENTINEL_STATES_0_NAME) can override configuration values.

You can explicitly set the configuration file path using sentinel.SetConfigFile("your/path/to/config.yaml") before calling sentinel.Init().

Usage & API

The sentinel library offers a set of exposed functions for users to seamlessly integrate and control its behavior. Here's a breakdown of the key functions and their uses:

  • Init(): This function is crucial for initializing the library, reading the configuration, and setting up the internal components. It should be called once at the beginning of your application's lifecycle. For example:

    if err := sentinel.Init(); err != nil {
        log.Fatalf("Failed to initialize sentinel: %v", err)
    }
    
  • SetConfigFile(path string): Use this to explicitly define the path to your configuration file (e.g., config.yaml). If not set, Sentinel will look for config.yaml in standard locations.

    sentinel.SetConfigFile("./my_custom_config.yaml")
    
  • SetLogLevel(level zerolog.Level): This allows you to control the verbosity of the library's logging output. You can set it to levels like zerolog.InfoLevel, zerolog.DebugLevel, or zerolog.ErrorLevel.

    sentinel.SetLogLevel(zerolog.DebugLevel)
    
  • RegisterCheckHandler(name string, handler CheckHandler): This function is used to register custom functions that will be executed as "checks" within your defined states. The name provided here should match the check name specified in your configuration.

    sentinel.RegisterCheckHandler("my_custom_check", myCheckLogic)
    
  • RegisterStateHandler(name string, handler StateHandler): Similar to check handlers, this allows you to register custom functions that will be called when the system enters a specific state. The name should correspond to a state defined in your configuration.

    sentinel.RegisterStateHandler("initial_state", myStateEntryLogic)
    
  • Start(ctx context.Context): This is the main entry point to run the Sentinel service. It will block until the provided context is cancelled, orchestrating state transitions and check execution as defined in your configuration.

    ctx, cancel := context.WithCancel(context.Background())
    // In a real application, you'd typically handle OS signals to cancel the context
    go func() {
        // Simulate some condition to stop the service
        time.Sleep(10 * time.Minute)
        cancel()
    }()
    sentinel.Start(ctx)
    
  • Next(): Transition to the next state

    for {
    	  select {
    	  case s := <-sigChan:
    		  switch s {
    		  case syscall.SIGUSR1:
    			  log.Info().Msg("SIGUSR1 received, rotating to the next state")
    			  sentinel.Next()
        //...
        }
      }
    }
    
  • GetCurrentState(): This function allows you to retrieve the name of the currently active state.

    currentState := sentinel.GetCurrentState()
    fmt.Printf("Current active state: %s\n", currentState)
    
Example config.yaml

# States define long-running processes that go-sentinel manages.
states:
  - name:
    type: cmd
    params:
      cmd: '/usr/bin/cvlc --loop /mnt/fileserver/asdasd'
  - name2:
    type: mycustomhandler
    params:
      cmd: '/usr/bin/fbi {{path}}'
      refreshinterval: 5s
      extensionfilter:
        - jpg
        - png
# Checks define checks that make sure the dependcies of the States are in a healthy condition
checks:
  - name: mount
    frequency:
      type: oneshot
    # Actions with type "mount" will attemt to run "mount -a" if the specified entry is found in fstab, logging an error otherwise
    action:
      type: mount
      params:
        device: //domain.example.com/smbshare
        dir: /mnt/fileserver
  - name: vlccheck
    frequency:
      type: periodic
      time: 5m
    condition:
      state: name
    # Actions with type "process" will restart the current check if the specified process is not running
    action:
      type: process
      params:
        procname: '/usr/bin/clvc'

🛠️ Development

Project Structure

  • /: Entry point, handles configuration loading, signal handling, and orchestrates the state and check managers.
  • config/: Defines the structure for go-sentinel's YAML configuration.
  • state/: Manages the lifecycle of long-running processes ("states"), including starting, stopping, and transitioning between them.
  • state/handlers: Implements the check handlers.
  • check/: Implements the check scheduling logic and defines interfaces for custom check handlers.
  • check/handlers: Implements the check handlers.
  • utils/: Contains utility functions (e.g., for checking mount status).

Adding New State Handlers

To extend go-sentinel with new state types, implement the StateHandler interface defined in state/handlers/handlers.go:

// state/handlers/handlers.go
type StateHandler interface {
    Start(ctx context.Context, state config.State, log *zerolog.Logger) (*exec.Cmd, error)
    Stop(cmd *exec.Cmd, log *zerolog.Logger) error
    Restart(ctx context.Context, oldCmd *exec.Cmd, state config.State, log *zerolog.Logger) (*exec.Cmd, error)
}

Then, register your new handler using sentinel.RegisterStateHandler in your main function

sentinel.RegisterStateHandler("mycustomhandler", &MyCustomStateHandler{})

Adding New Check Handlers

To extend go-sentinel with new functionality, implement the CheckHandler interface:

// check/registry.go
type CheckHandler interface {
    Execute(ctx context.Context, log *zerolog.Logger, params map[string]interface{}) error
}

Then, register your new handler using sentinel.RegisterCheckHandler in your main function


 sentinel.RegisterCheckHandler("your_new_action_type", &MyCustomCheckHandler{})


Documentation

Overview

Copyright © 2025 Alex Bedo <alex98hun@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Index

Constants

This section is empty.

Variables

View Source
var (
	CheckScheduler *check.Scheduler
)

Functions

func GetCurrentState

func GetCurrentState() string

func Init

func Init() error

Init initializes the configuration and sets up logging. It returns an error if initialization fails.

func Next

func Next()

func RegisterCheckHandler

func RegisterCheckHandler(name string, handl CheckHandler)

func RegisterStateHandler

func RegisterStateHandler(name string, handl handlers.StateHandlerFactory)

func SetConfigFile

func SetConfigFile(conf string)

SetConfigFile explicitly defines the path, name and extension of the config file.

func SetLogLevel

func SetLogLevel(lvl zerolog.Level)

func Start

func Start(ctx context.Context)

Start runs the main logic of the service, including state management and Check scheduling. It blocks until a termination signal is received or the context is cancelled.

Types

type CheckHandler

type CheckHandler = check.CheckHandler

type NoStatesError

type NoStatesError struct{}

NoStatesError is a custom error type for when no states are configured.

func (*NoStatesError) Error

func (e *NoStatesError) Error() string

type StateHandlerFactory

type StateHandlerFactory = state.StateHandlerFactory

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL