glcm

package module
v0.0.0-...-534781b Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2025 License: MIT Imports: 18 Imported by: 0

README

glcm: Go Routine Lifecycle Management


      ___           ___       ___           ___
     /\  \         /\__\     /\  \         /\__\
    /::\  \       /:/  /    /::\  \       /::|  |
   /:/\:\  \     /:/  /    /:/\:\  \     /:|:|  |
  /:/  \:\  \   /:/  /    /:/  \:\  \   /:/|:|__|__
 /:/__/_\:\__\ /:/__/    /:/__/ \:\__\ /:/ |::::\__\
 \:\  /\ \/__/ \:\  \    \:\  \  \/__/ \/__/~~/:/  /
  \:\ \:\__\    \:\  \    \:\  \             /:/  /
   \:\/:/  /     \:\  \    \:\  \           /:/  /
    \::/  /       \:\__\    \:\__\         /:/  /
     \/__/         \/__/     \/__/         \/__/

glcm is a Go package designed to manage the complete lifecycle of goroutines, providing a structured approach to starting, stopping, and monitoring services within your Go applications.

Features

  • Service Registration: Register multiple services to be managed concurrently.
  • Lifecycle Management: Control the startup and shutdown sequences of all registered services.
  • Service Control: Individually start, stop, and restart services as needed.
  • Hooks Integration: Define pre-run and post-run hooks for services to execute custom logic before starting or after stopping a service.
  • Auto-Restart with Backoff: Automatically restart services with optional exponential backoff.

Installation

To install the package, run:

go get github.com/achu-1612/glcm

Usage

Here's how to use glcm in your project:

1. Import the package
import "github.com/achu-1612/glcm"
2. Create a new runner
ctx := context.Background()
runner := glcm.NewRunner(ctx, glcm.RunnerOptions{})
3. Define a service

Implement the Service interface for your service. This interface requires the following methods:

  • Start(Terminator): Defines the startup logic for the service.
  • Status() string: Status returns the status of the service.
  • Name() string: Returns the name of the service.

Example:

type MyService struct{}

func (m *MyService) Start(ctx service.Terminator) {
    // Initialization logic here
    // Start should be a blocking call.
    // On closing of the ctx.TermCh() channel, the method should return.

    // example: 
    for {
		<-time.After(time.Second * 2)

		select {
		case <-ctx.TermCh():
			return
		default:
			log.Println("service is running ", time.Now())
	}
	
}

func (m *MyService) Name() error {
    return "MyService"
}

func (m *MyService) Status() string {
    return ""
}
4. Register the service
err := runner.RegisterService(&MyService{}, glcm.ServiceOptions{})
if err != nil {
    // Handle error
}
5. Boot up the runner
// BootUp boots up the runner. This will start all the registered services.
//Note: This is a blocking call. It is to be called after BootUp.
// Only a ShutDown() call will stop the runner.
// Even after all the registered services are stopped, runner would 
if err := runner.BootUp(); err != nil {
    log.Fatalf("Error while booting up the runner: %v", err)
}
6. Shutdown the runner
// Shutdown shuts down the runner. This will stop all the registered services.
runner.Shutdown()
7. Stop service(s)
// StopService stops the given list of services.
runner.StopService("MyService1", "MyService2")

// StopAllServices stops all the registered/running services.
runner.StopAllServices()
8. Restart service(s)
// RestartService restarts the given list of services.
runner.RestartService("MyService1", "MyService2")

// RestartAllServices restarts all the registered/running services.
runner.RestartAllServices()

Auto-Restart with Backoff

To enable auto-restart with backoff for a service, use the following options during service registration: Note: The service will be restarted automatically only when service.WithAutoRestart() options is given while service registration and when the service exits automatically not by runner shutting it down.

err := runner.RegisterService(
    &MyService{},
    glcm.ServiceOptions{
        AutoStart: glcm.AutoRestartOptions{
            Enabled:    true,
            Backoff:    true,
            MaxRetries: 5, // Optional: Set maximum retries
            BackOffExponent: 2, // Optional: Set backoff exponent
        },
    },
)
if err != nil {
    // Handle error
}

Service Hooks

The hook package allows you to define hooks that execute before or after a service starts.

1. Create a new hook handler
preRunHook := hook.NewHook("PreRunHook", func(args ...interface{}) error {
    // Pre-run logic here
    return nil
})
2. Register the service with hooks
err := runner.RegisterService(
    &MyService{}, 
    glcm.ServiceOptions{
        PreHooks: []glcm.Hook{preRunHook},
        PostHooks: []glcm.Hook{postRunHook},
    },
)
if err != nil {
    // Handle error
}

Socket Usage

glcm supports socket communication for both Windows and Linux platforms. This allows you to send commands to control the lifecycle of services.

Windows

On Windows, glcm uses named pipes for socket communication.

Linux

On Linux, glcm uses Unix domain sockets for socket communication.

Allowed Messages and Actions

The following messages can be sent to the socket to control the services:

  • restart <service_name>: restart the specified service
  • stop <service_name>: stop the specified service.
  • restartAll <service_name>: restart all the services.
  • stopAll <service_name>: stop all the services.
  • list: list all the service and their current status.
Example Usage
echo "restartAll" | socat - UNIX-CONNECT:/tmp/glcm.sock
// Example for Linux Unix domain socket communication
socketPath := "/tmp/glcm_socket"
conn, err := net.Dial("unix", socketPath)
if err != nil {
    log.Fatalf("Failed to connect to socket: %v", err)
}
defer conn.Close()

_, err = conn.Write([]byte("start MyService"))
if err != nil {
    log.Fatalf("Failed to send message: %v", err)
}

Contributing

Contributions are welcome! Please submit issues and pull requests for any improvements or bug fixes.

License

This project is licensed under the MIT License.

TODO

  • Support for Job with scheduling.
  • Support for timeout for go-routine shutdowns (if possible).
  • Better error handling for the pre and post hooks for service.
  • Service dependency.

Documentation

Overview

Package glcm manage life cycle multiple go routines.

Package glcm is a generated GoMock package.

Index

Constants

View Source
const (
	SocketActionStopAllServices socketAction = "stopAll"
	SocketActionStopService     socketAction = "stop"
	SocketActionRestartAll      socketAction = "restartAll"
	SocketActionRestartService  socketAction = "restart"
	SocketActionStatus          socketAction = "status"
)

list of supported socket action commands

View Source
const (
	Success socketCommandStatus = "success"
	Failure socketCommandStatus = "failure"
)

list of supported socket command status

Variables

View Source
var (
	ErrRegisterServiceAlreadyExists = errors.New("service already exists")
	ErrDeregisterServiceNotFound    = errors.New("service not found")
	ErrRunnerAlreadyRunning         = errors.New("runner already running")
	ErrRegisterNilService           = errors.New("can not register nil service")
	ErrUnsupportedOS                = errors.New("unsupported OS")
	ErrSocketNoService              = errors.New("no service provided")
)
View Source
var (
	ErrServiceNotRunning = errors.New("service not running")
)

Functions

This section is empty.

Types

type AutoRestart

type AutoRestart struct {
	Enabled         bool        // flag to indicate if auto-restart is enabled.
	MaxRetries      int         // maximum number of retries.
	Backoff         bool        // flag to indicate if backoff is enabled.
	BackoffExponent int         // exponent for the backoff.
	RetryCount      int         // current number of retries for the service.
	PendingStart    atomic.Bool // flag to indicate if the service is pending for a start after the backoff.
}

AutoRestart is the configuration set for auto-restart.

type AutoRestartOptions

type AutoRestartOptions struct {
	// Enabled represents if the auto-restart is enabled.
	Enabled bool

	// MaxRetries represents the maximum number of retries.
	MaxRetries int

	// Backoff represents if the backoff is enabled.
	Backoff bool

	// BackOffExponent represents the exponent for the backoff.
	BackOffExponent int
}

AutoRestartOptions represents the options for auto-restarting the service.

type Hook

type Hook interface {
	// Execute executes the hook method.
	Execute() error

	// Name returns the name of the hook.
	Name() string
}

Hook is an interface which represents a single hook. When a servcice is regsited in the runner, implementations of the Hndler interface can be registered too. Based on the nature of the hook (pre-run or post-run), the hook will be executed.

func NewHook

func NewHook(name string, f func(...interface{}) error, args ...interface{}) Hook

NewHook returns a new instance of the Hook.

type MockHook

type MockHook struct {
	// contains filtered or unexported fields
}

MockHook is a mock of Hook interface.

func NewMockHook

func NewMockHook(ctrl *gomock.Controller) *MockHook

NewMockHook creates a new mock instance.

func (*MockHook) EXPECT

func (m *MockHook) EXPECT() *MockHookMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockHook) Execute

func (m *MockHook) Execute() error

Execute mocks base method.

func (*MockHook) Name

func (m *MockHook) Name() string

Name mocks base method.

type MockHookMockRecorder

type MockHookMockRecorder struct {
	// contains filtered or unexported fields
}

MockHookMockRecorder is the mock recorder for MockHook.

func (*MockHookMockRecorder) Execute

func (mr *MockHookMockRecorder) Execute() *gomock.Call

Execute indicates an expected call of Execute.

func (*MockHookMockRecorder) Name

func (mr *MockHookMockRecorder) Name() *gomock.Call

Name indicates an expected call of Name.

type MockRunner

type MockRunner struct {
	// contains filtered or unexported fields
}

MockRunner is a mock of Runner interface.

func NewMockRunner

func NewMockRunner(ctrl *gomock.Controller) *MockRunner

NewMockRunner creates a new mock instance.

func (*MockRunner) BootUp

func (m *MockRunner) BootUp() error

BootUp mocks base method.

func (*MockRunner) DeregisterService

func (m *MockRunner) DeregisterService(arg0 string) error

DeregisterService mocks base method.

func (*MockRunner) EXPECT

func (m *MockRunner) EXPECT() *MockRunnerMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockRunner) IsRunning

func (m *MockRunner) IsRunning() bool

IsRunning mocks base method.

func (*MockRunner) RegisterService

func (m *MockRunner) RegisterService(arg0 Service, arg1 ServiceOptions) error

RegisterService mocks base method.

func (*MockRunner) RestartAllServices

func (m *MockRunner) RestartAllServices()

RestartAllServices mocks base method.

func (*MockRunner) RestartService

func (m *MockRunner) RestartService(arg0 ...string) error

RestartService mocks base method.

func (*MockRunner) Shutdown

func (m *MockRunner) Shutdown()

Shutdown mocks base method.

func (*MockRunner) Status

func (m *MockRunner) Status() *RunnerStatus

Status mocks base method.

func (*MockRunner) StopAllServices

func (m *MockRunner) StopAllServices()

StopAllServices mocks base method.

func (*MockRunner) StopService

func (m *MockRunner) StopService(arg0 ...string) error

StopService mocks base method.

type MockRunnerMockRecorder

type MockRunnerMockRecorder struct {
	// contains filtered or unexported fields
}

MockRunnerMockRecorder is the mock recorder for MockRunner.

func (*MockRunnerMockRecorder) BootUp

func (mr *MockRunnerMockRecorder) BootUp() *gomock.Call

BootUp indicates an expected call of BootUp.

func (*MockRunnerMockRecorder) DeregisterService

func (mr *MockRunnerMockRecorder) DeregisterService(arg0 interface{}) *gomock.Call

DeregisterService indicates an expected call of DeregisterService.

func (*MockRunnerMockRecorder) IsRunning

func (mr *MockRunnerMockRecorder) IsRunning() *gomock.Call

IsRunning indicates an expected call of IsRunning.

func (*MockRunnerMockRecorder) RegisterService

func (mr *MockRunnerMockRecorder) RegisterService(arg0, arg1 interface{}) *gomock.Call

RegisterService indicates an expected call of RegisterService.

func (*MockRunnerMockRecorder) RestartAllServices

func (mr *MockRunnerMockRecorder) RestartAllServices() *gomock.Call

RestartAllServices indicates an expected call of RestartAllServices.

func (*MockRunnerMockRecorder) RestartService

func (mr *MockRunnerMockRecorder) RestartService(arg0 ...interface{}) *gomock.Call

RestartService indicates an expected call of RestartService.

func (*MockRunnerMockRecorder) Shutdown

func (mr *MockRunnerMockRecorder) Shutdown() *gomock.Call

Shutdown indicates an expected call of Shutdown.

func (*MockRunnerMockRecorder) Status

func (mr *MockRunnerMockRecorder) Status() *gomock.Call

Status indicates an expected call of Status.

func (*MockRunnerMockRecorder) StopAllServices

func (mr *MockRunnerMockRecorder) StopAllServices() *gomock.Call

StopAllServices indicates an expected call of StopAllServices.

func (*MockRunnerMockRecorder) StopService

func (mr *MockRunnerMockRecorder) StopService(arg0 ...interface{}) *gomock.Call

StopService indicates an expected call of StopService.

type MockService

type MockService struct {
	// contains filtered or unexported fields
}

MockService is a mock of Service interface.

func NewMockService

func NewMockService(ctrl *gomock.Controller) *MockService

NewMockService creates a new mock instance.

func (*MockService) EXPECT

func (m *MockService) EXPECT() *MockServiceMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockService) Name

func (m *MockService) Name() string

Name mocks base method.

func (*MockService) Start

func (m *MockService) Start(arg0 Terminator)

Start mocks base method.

type MockServiceMockRecorder

type MockServiceMockRecorder struct {
	// contains filtered or unexported fields
}

MockServiceMockRecorder is the mock recorder for MockService.

func (*MockServiceMockRecorder) Name

func (mr *MockServiceMockRecorder) Name() *gomock.Call

Name indicates an expected call of Name.

func (*MockServiceMockRecorder) Start

func (mr *MockServiceMockRecorder) Start(arg0 interface{}) *gomock.Call

Start indicates an expected call of Start.

type MockTerminator

type MockTerminator struct {
	// contains filtered or unexported fields
}

MockTerminator is a mock of Terminator interface.

func NewMockTerminator

func NewMockTerminator(ctrl *gomock.Controller) *MockTerminator

NewMockTerminator creates a new mock instance.

func (*MockTerminator) EXPECT

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockTerminator) TermCh

func (m *MockTerminator) TermCh() chan struct{}

TermCh mocks base method.

type MockTerminatorMockRecorder

type MockTerminatorMockRecorder struct {
	// contains filtered or unexported fields
}

MockTerminatorMockRecorder is the mock recorder for MockTerminator.

func (*MockTerminatorMockRecorder) TermCh

func (mr *MockTerminatorMockRecorder) TermCh() *gomock.Call

TermCh indicates an expected call of TermCh.

type MockWrapper

type MockWrapper struct {
	// contains filtered or unexported fields
}

MockWrapper is a mock of Wrapper interface.

func NewMockWrapper

func NewMockWrapper(ctrl *gomock.Controller) *MockWrapper

NewMockWrapper creates a new mock instance.

func (*MockWrapper) AutoRestart

func (m *MockWrapper) AutoRestart() *AutoRestart

AutoRestart mocks base method.

func (*MockWrapper) EXPECT

func (m *MockWrapper) EXPECT() *MockWrapperMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockWrapper) Name

func (m *MockWrapper) Name() string

Name mocks base method.

func (*MockWrapper) Start

func (m *MockWrapper) Start()

Start mocks base method.

func (*MockWrapper) Status

func (m *MockWrapper) Status() ServiceStatus

Status mocks base method.

func (*MockWrapper) Stop

func (m *MockWrapper) Stop()

Stop mocks base method.

func (*MockWrapper) TermCh

func (m *MockWrapper) TermCh() chan struct{}

TermCh mocks base method.

func (*MockWrapper) Uptime

func (m *MockWrapper) Uptime() time.Duration

Uptime mocks base method.

type MockWrapperMockRecorder

type MockWrapperMockRecorder struct {
	// contains filtered or unexported fields
}

MockWrapperMockRecorder is the mock recorder for MockWrapper.

func (*MockWrapperMockRecorder) AutoRestart

func (mr *MockWrapperMockRecorder) AutoRestart() *gomock.Call

AutoRestart indicates an expected call of AutoRestart.

func (*MockWrapperMockRecorder) Name

func (mr *MockWrapperMockRecorder) Name() *gomock.Call

Name indicates an expected call of Name.

func (*MockWrapperMockRecorder) Start

func (mr *MockWrapperMockRecorder) Start() *gomock.Call

Start indicates an expected call of Start.

func (*MockWrapperMockRecorder) Status

func (mr *MockWrapperMockRecorder) Status() *gomock.Call

Status indicates an expected call of Status.

func (*MockWrapperMockRecorder) Stop

func (mr *MockWrapperMockRecorder) Stop() *gomock.Call

Stop indicates an expected call of Stop.

func (*MockWrapperMockRecorder) TermCh

func (mr *MockWrapperMockRecorder) TermCh() *gomock.Call

TermCh indicates an expected call of TermCh.

func (*MockWrapperMockRecorder) Uptime

func (mr *MockWrapperMockRecorder) Uptime() *gomock.Call

Uptime indicates an expected call of Uptime.

type Runner

type Runner interface {
	// IsRunning returns true if the runner is running, otherwise false.
	IsRunning() bool

	// RegisterService registers a service with the runner.
	RegisterService(Service, ServiceOptions) error

	// DeregisterService deregisters a service from the runner.
	DeregisterService(string) error

	// Shutdown stops all the services and the runner.
	Shutdown()

	// StopAllServices stops all the services.
	StopAllServices()

	// StopService stops the specified services.
	StopService(...string) error

	// RestartService restarts the specified services.
	RestartService(...string) error

	// RestartAllServices restarts all the services.
	RestartAllServices()

	// BootUp starts the runner.
	BootUp() error

	// Status returns the status of the runner along with the status of each registered service.
	Status() *RunnerStatus
}

Runner represents the interface for the base runner methods.

func NewRunner

func NewRunner(ctx context.Context, opts RunnerOptions) Runner

NewRunner returns a new instance of the runner.

type RunnerOptions

type RunnerOptions struct {
	// HideBanner represents if the banner should be hidden.
	HideBanner bool

	// Verbose represents if the logs should be suppressed or not.
	Verbose bool

	// Socket represents if the socket should be enabled or not.
	Socket bool

	// SocketPath represents the path to the socket file.
	SocketPath string

	// AllowedUID represents the allowed user ids to interact with the socket.
	AllowedUID []int

	// ShutdownTimeout represents the timeout for shutting down the runner.
	ShutdownTimeout time.Duration
}

RunnerOptions represents the options for the runner.

func (*RunnerOptions) Sanitize

func (r *RunnerOptions) Sanitize()

Santizie fills the default values for the runner options.

type RunnerStatus

type RunnerStatus struct {
	IsRunning bool                   `json:"isRunning"`
	Services  map[string]ServiceInfo `json:"services"`
}

RunnerStatus represents the status of the runner.

type SchedulingOptions

type SchedulingOptions struct {
	// Enabled represents if the auto-restart is enabled.
	Enabled bool

	// Cron represents the cron expression for scheduling the service.
	Cron string

	// TimeOut represents the timeout for the service.
	// After the timeout, the service will be sent a termination signal.
	TimeOut time.Duration

	// MaxRuns represents the maximum number of runs for the service.
	MaxRuns int
}

SchedulingOptions represents the options for scheduling the service.

type Service

type Service interface {
	// Name returns the name of the service.
	Name() string

	// Start executes/boots-up/starts a service.
	Start(Terminator)
}

Service defines an interface which represents a single service and the operations that can be performed on the service. Note: The service should be able to handle the termination signal. At this point, I don't think we need to have a Stop or Restart method. Once the termincation channel is closed, the service should stop. If the service needs to be restarted, the runner will take care of it, internally.

type ServiceInfo

type ServiceInfo struct {
	Status   ServiceStatus `json:"status"`
	Uptime   time.Duration `json:"uptime"`
	Restarts int           `json:"restarts"`
}

ServiceStatus represents the available information of the service.

type ServiceOptions

type ServiceOptions struct {
	// PreHooks are the hooks that are executed before the service is started.
	PreHooks []Hook

	// PostHooks are the hooks that are executed after the service is stopped.
	PostHooks []Hook

	// AutoStart represents the options for auto-starting the service.
	AutoStart AutoRestartOptions

	// Schedule represents the options for scheduling the service.
	Schedule SchedulingOptions
}

ServiceOptions represents the options for a service.

func (*ServiceOptions) Sanitize

func (s *ServiceOptions) Sanitize()

Sanitize fills the default values for the service options.

type ServiceStatus

type ServiceStatus string

ServiceStatus represents the status of the service.

const (
	ServiceStatusRegistered          ServiceStatus = "registered"
	ServiceStatusRunning             ServiceStatus = "running"
	ServiceStatusExited              ServiceStatus = "exited"
	ServiceStatusStopped             ServiceStatus = "stopped"
	ServiceStatusScheduled           ServiceStatus = "scheduled"
	ServiceStatusScheduledForRestart ServiceStatus = "scheduled-for-restart"
	ServiceStatusExhausted           ServiceStatus = "exhausted"
)

Status options for the service.

type SocketResponse

type SocketResponse struct {
	Result interface{}         `json:"result"`
	Status socketCommandStatus `json:"status"`
}

SocketResponse represents the response from the socket.

type Terminator

type Terminator interface {
	// TermCh returns a channel which will be closed when the service should stop.
	TermCh() chan struct{}
}

Terminator defines an indicator to the service to stop.

type Wrapper

type Wrapper interface {
	// Name returns the name of the service.
	Name() string

	// Status returns the status of the service/wrapper.
	Status() ServiceStatus

	// TermCh returns the termination channel for the service.
	TermCh() chan struct{}

	// Start starts the services in the wrapper.
	Start()

	// Stop stops the service in the wrapper and waits for the service to stop.
	Stop()

	// AutoRestart returns the auto-restart configuration for the wrapper.
	AutoRestart() *AutoRestart

	// Uptime returns the uptime of the service.
	Uptime() time.Duration
}

Wrapper is an interface which represents the wraper around the service.

func NewWrapper

func NewWrapper(s Service, wg *sync.WaitGroup, opts ServiceOptions) Wrapper

NewWrapper returns a new instance of the service Wrapper.

Directories

Path Synopsis
cmd
cli command
example
advanced command
auto-restart command
backoff command
bootup command
restart command
socket command
stop command

Jump to

Keyboard shortcuts

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