actions

package
v0.0.0-...-a4d1c61 Latest Latest
Warning

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

Go to latest
Published: Jan 6, 2023 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DeploymentJobInput entries contain the data used as input for the job.
	DeploymentJobInput = deploymentDataType("input")
	// DeploymentJobData entries contain data stored by a job for future use.
	// This data is used by jobs to handle errors and rollback.
	DeploymentJobData = deploymentDataType("job")
)

Variables

View Source
var (
	// NilJobDataType is used to indicate that a Job Input or Output does not receive or return data.
	NilJobDataType = reflect.TypeOf(struct{}{})

	// ErrJobsEmptySequence is returned when a Jobs object does not contain any jobs.
	ErrJobsEmptySequence = errors.New("job sequence is empty")
	// ErrJobsNamesNotUnique is returned when a collection of Jobs contains more than one job with the same name.
	ErrJobsNamesNotUnique = errors.New("jobs names must be unique")
)
View Source
var (
	// ErrGenerateNoActionName is raised when an application action name generation does not contain an action name.
	ErrGenerateNoActionName = errors.New("tried to generate an application action name with no action name")
	// ErrActionIsNil is raised when a nil action is registered.
	ErrActionIsNil = errors.New("action is nil")
	// ErrActionExists is raised when an action is registered but it already exists.
	ErrActionExists = errors.New("action already exists")
	// ErrActionNotFound is raised when a requested action is not registered in the service.
	ErrActionNotFound = errors.New("action not found")
	// ErrJobNilOutput is raised when a job returns nil
	ErrJobNilOutput = errors.New("job cannot return nil, pass through the input value instead")
	// ErrExecutionStopped is raised when the execution is forcibly stopped by a user command.
	ErrExecutionStopped = errors.New("action execution was stopped by a user command")
)
View Source
var (
	// ErrExecuteInputRestoreNoAction is returned when an ExecuteInput restore is attempted without providing an action
	ErrExecuteInputRestoreNoAction = errors.New("cannot restore an execute input with no action")
	// ErrExecuteInputRestoreNoDeployment is returned when an ExecuteInput restore is attempted without providing a
	// deployment
	ErrExecuteInputRestoreNoDeployment = errors.New("cannot restore an execute input with no deployment")
)
View Source
var (
	// ErrDeploymentDataNoData is returned when job data was requested by the entry contained no data
	ErrDeploymentDataNoData = errors.New("an entry for the type and job was found, but there is no data")
)
View Source
var (
	// ErrJobDataTypeNotFound is returned when a job data type was not found.
	// This is likely indicative of a problem in a job's InputType or OutputType properties.
	ErrJobDataTypeNotFound = errors.New("job data type not found")
)

Functions

func CleanAndMigrateDB

func CleanAndMigrateDB(tx *gorm.DB) error

CleanAndMigrateDB cleans and migrates action database models, indexes and keys.

func DropDB

func DropDB(tx *gorm.DB) error

DropDB drops action database models, indexes and keys.

func ErrorHandlerIgnoreError

func ErrorHandlerIgnoreError(store Store, tx *gorm.DB, deployment *Deployment, value interface{},
	err error) (interface{}, error)

ErrorHandlerIgnoreError ignores errors returned by a function and continues execution.

func GetJobDataTypeName

func GetJobDataTypeName(value interface{}) string

GetJobDataTypeName returns the data type name for a job input or output.

func MigrateDB

func MigrateDB(tx *gorm.DB) error

MigrateDB migrates action database models, indexes and keys.

Types

type Action

type Action struct {
	// Name contains the action name.
	// This field can be left empty as it will be filled in by a service when registering this action.
	Name string
	// Jobs contains the sequence of jobs processed to perform this action.
	Jobs Jobs
}

Action contains a sequence of jobs, and performs a specific function. Actions are registered, launched and managed by action services. Action instances should only be used to define a sequence of states for registration in a service.

func NewAction

func NewAction(jobs Jobs) (*Action, error)

NewAction creates a new Action containing a sequence of jobs.

type Deployment

type Deployment struct {
	gorm.Model
	// UUID contains the unique identifier of this deployment.
	UUID string `gorm:"not null"`
	// Action contains the action this deployment executes.
	Action string `gorm:"not null"`
	// Status contains the status of this deployment.
	Status DeploymentStatus `gorm:"not null"`
	// CurrentJob contains the current action job the deployment is executing.
	CurrentJob string `gorm:"not null"`
	// RollbackError contains the error that triggered the rollback of this deployment.
	RollbackError *string
}

Deployment contains persistent state of a single action execution. The stored state is used to resume actions in case of interruption (server restarts, etc.).

func (*Deployment) GetErrors

func (d *Deployment) GetErrors(tx *gorm.DB, job *string) (DeploymentErrors, error)

GetErrors returns a slice with all the deployment errors logged for this deployment. If `job` is not nil, this will return the errors for a single job, otherwise this will return errors for all jobs.

func (*Deployment) GetJobData

func (d *Deployment) GetJobData(tx *gorm.DB, job *string, dataType deploymentDataType) (interface{}, error)

GetJobData gets job data entry of a specific type for a job in this deployment.

func (*Deployment) GetJobDataOutValue

func (d *Deployment) GetJobDataOutValue(tx *gorm.DB, job *string, dataType deploymentDataType, out interface{}) error

GetJobDataOutValue gets job data entry for a job in this deployment and stores the result in the passed output value. `out` must be a pointer.

func (*Deployment) SetJobData

func (d *Deployment) SetJobData(tx *gorm.DB, job *string, dataType deploymentDataType, data interface{}) error

SetJobData creates a job data entry of a specific type for a job in this deployment.

func (Deployment) TableName

func (Deployment) TableName() string

TableName sets the database table name for Deployment.

type DeploymentDataSet

type DeploymentDataSet []*deploymentData

DeploymentDataSet is a slice of deploymentData pointers.

type DeploymentError

type DeploymentError struct {
	gorm.Model
	DeploymentID int
	Deployment   *Deployment
	Job          *string
	Error        *string `gorm:"type:text"`
}

DeploymentError contains a single deployment job error. A job can contain multiple error entries.

func (DeploymentError) TableName

func (de DeploymentError) TableName() string

TableName sets the database table name for DeploymentError.

type DeploymentErrors

type DeploymentErrors []*DeploymentError

DeploymentErrors is a slice of DeploymentError pointers.

type DeploymentStatus

type DeploymentStatus string

DeploymentStatus is a possible status for a Deployment.

type Deployments

type Deployments []*Deployment

Deployments is a slice of Deployment pointers.

func GetRunningDeployments

func GetRunningDeployments(tx *gorm.DB) (Deployments, error)

GetRunningDeployments returns the set of deployments that are still running.

type ExecuteInput

type ExecuteInput struct {
	// GroupID is the universal unique identifier of the deployment that will be created.
	GroupID string
	// ApplicationName is the name of the application of the action to execute.
	ApplicationName *string
	// ActionName is the name of the action to execute.
	ActionName string
	// Deployment contains an optional action deployment.
	// If this value is not `nil`, then the action will be restarted.
	Deployment *Deployment
	// contains filtered or unexported fields
}

ExecuteInput contains the set of fields that all action execution inputs must contain. An action's custom execution input can compose a pointer to this struct (i.e. *ExecuteInput) to automatically implement the required set of fields.

type ExecuteInputer

type ExecuteInputer interface {
	// contains filtered or unexported methods
}

ExecuteInputer is an interface that action execution inputs must implement. This interface allows having a common set of data shared by all action-specific input types.

type Job

type Job struct {
	// Name contains the name of the Job. This value should be unique as it will be the Job identifier.
	Name string `validate:"required"`
	// PreHooks contains the hooks ran before the Execute function. Used to validate/preprocess job input data.
	PreHooks []JobFunc
	// Execute performs this job's action.
	// It should always return a value (not nil), not doing so will result in an error.
	Execute JobFunc `validate:"required"`
	// PostHooks contains the hooks ran after the Execute function. Used to validate/postprocess job output data.
	PostHooks []JobFunc
	// RollbackHandler contains the rollback function for this job.
	RollbackHandler JobErrorHandler
	// InputType contains the input type this job receives.
	// This should contain the return value of calling `GetJobDataType` with a value of the expected type.
	InputType JobDataType `validate:"required"`
	// OutputType contains the output type this job returns.
	// This should contain the return value of calling `GetJobDataType` with a value of the expected type.
	OutputType JobDataType `validate:"required"`
}

Job is the base atomic component in an Action. It contains the instructions necessary to perform and rollback a single operation. A Job is composed of three parts run in succession: PreHooks, Execute function and PostHooks. When ran (by calling Job.Run), a Job goes through a sequence of internal steps:

  1. A number of optional PreHook functions are called to perform validation and preprocessing operations on the Execute function input data.
  2. The Execute function is called to perform an operation with the input received from the PreHook functions.
  3. A number of optional PostHook functions are called to perform validation and postprocessing operations on the Execute function output data before handing off the data to the next Job in the sequence.

Hooks allow fitting a Job for application-specific scenarios. As an example, a Job that launches cloud instances needs to get the list of type of instances it needs to launch. Different applications can use hooks to specify this information and use the same execute logic to launch instances specific to their applications.

Each Job should only perform one write or update operation, and it should only be performed in the Execute function. If more than one operation is needed, or a hook needs to write or update a persistent entry, consider creating additional jobs to handle the operation. Doing so will greatly simplify error and rollback handling. To create new jobs for an application, instance this struct. If an existing Job is to be fitted for an application, Job.Extend can be used to get an application-specific version of the Job.

If a Job function (PreHook, Execute Funcion, PostHook) returns an error, the error is logged and the execution of the sequence of jobs stops and is rolled back. Job functions can handle errors by wrapping functions with error handlers by calling WrapErrorHandler. Job functions with error handlers allow logging errors and recovering from them. If a function or error-handled function returns an error, this will trigger a rollback of the entire job sequence.

A Job may contain an optional RollbackHandler function. The RollbackHandler function is in charge of releasing any shared resources claimed (e.g. cloud instances, orchestration resources, etc.) and undoing any changes that may impact other operations. Rollback logic should always double check to understand the state of things, as the job may be rolled back before any resources are allocated. For example, the rollback logic for a job tasked with requesting cloud instances should check that there are machines allocated at all before proceeding to request the termination of said machines.

The RollbackHandler function may require context from the Execute function to perform its operations (e.g. instance ids, pod ids, etc.). This shared context should be stored by the Execute function by calling deployment.SetJobData and creating a `deploymentData` type entry. The shared context can then be retrieved by the RollbackHandler by calling deployment.GetJobData.

Jobs contain InputType and OutputType fields. These required fields contain the data types expected of the values received and returned by the Job, respectively. They are used to automate marhsalling and unmarshalling from persistent storage.

The InputType and OutputType fields values should be obtained by calling GetJobDataType with a value of the type required by the Job. If a job does not require any input data, or does not return any input data, the special value `NilJobDataType` can be used to indicate such. Note that this does not mean that the job will not receive or return data; this information lets an actions.Service instance know how to recover data from the persistent records.

As an illustration, consider the following scenario:

┌────┐                 ┌────┐                 ┌────┐

-string→ │ S1 │ -ExampleStruct→ │ S2 │ -ExampleStruct→ │ S3 │

         └────┘                 └────┘                 └────┘
	        I: string              I: Nil                 I: ExampleStruct
         O: ExampleStruct       O: Nil                 O: Nil

S1 receives a string as input and returns an ExampleStruct instance. S2 does not require any data to run, but still receives and returns the ExampleStruct received from S1. S3 receives the ExampleStruct from S2 and performs some operations based on it.

Since later jobs may require information from previous jobs, it is very important that all Jobs return a value. To enforce this, Actions will fail if no data is returned by a Job, even if the job has indicated that it returns no data. If a Job does not require or return any data to operate, it should pass along the data it receives from the previous job.

func (*Job) Extend

func (j *Job) Extend(extension Job) *Job

Extend customizes this job by modifying its hooks and error handlers. The extension cannot replace the Execute function. If you need to change the Execute function, you should create a new job instead.

func (Job) Rename

func (j Job) Rename(name string) *Job

Rename creates a copy of this job with a new name. Useful for when you want to run a job more than once in a single action.

func (*Job) Run

func (j *Job) Run(store Store, tx *gorm.DB, deployment *Deployment, value interface{}) (interface{}, error)

Run runs the job. It calls the job's pre-hooks, followed by its Execute method, and finally its post-hooks.

type JobDataType

type JobDataType reflect.Type

JobDataType is used to store Job input and output data types.

func GetJobDataType

func GetJobDataType(value interface{}) JobDataType

GetJobDataType returns the data type for a job input or output. This is used to properly load job data from the persistent storage.

type JobErrorHandler

type JobErrorHandler func(store Store, tx *gorm.DB, deployment *Deployment, value interface{},
	err error) (interface{}, error)

JobErrorHandler is the job function type called when an error occurs in a job.

type JobFunc

type JobFunc func(store Store, tx *gorm.DB, deployment *Deployment, value interface{}) (interface{}, error)

JobFunc is the function signature used by job hooks and Execute function.

func WrapErrorHandler

func WrapErrorHandler(fn JobFunc, errorHandler JobErrorHandler) JobFunc

WrapErrorHandler wraps a job function with an ErrorHandler. The wrapper also adds any errors returned by the job function or error handler. If `fn` returns an error, the error is handled by the `errorHandler` function. If the handler returns an error, the error is considered critical and triggers an action execution rollback.

type Jobs

type Jobs []*Job

Jobs is a slice of Job

type Servicer

type Servicer interface {
	// RegisterAction registers an action for a specific application.
	RegisterAction(applicationName *string, actionName string, action *Action) error
	// Execute executes an action.
	Execute(store Store, tx *gorm.DB, executeInput ExecuteInputer, jobInput interface{}) error
}

Servicer is the interface for action services.

func NewService

func NewService(logger gz.Logger) Servicer

NewService returns a pointer to an action Servicer implementation.

type State

type State interface{}

State represents the store states. It's used to save data, artifacts and dependencies that will be used across jobs.

type Store

type Store interface {
	// State returns the store's state.
	State() State

	// SetState changes the current state with the given state.
	SetState(state State)
}

Store groups a set of methods to interact with the state.

func NewStore

func NewStore(initialState State) Store

NewStore initializes a new store with the given initial state.

Jump to

Keyboard shortcuts

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