workflow

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2023 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package workflow defines workflow interfaces, types, and primitives.

Workflows

Workflows are a concept that abstracts away some of the lower-level tracking of Apple MDM "v1" command responses to better focus on accomplishing higher-level end goals.

To that end Workflows are, to the larger workflow engine, just MDM command senders (enqueuers) and receivers for MDM command responses (Result Reports). However the business of "routing" which MDM commands are for which purpose including additional metadata is taken care of for us in a so we can instead concentrate on the actully useful stuff (i.e. the business logic) of MDM commands.

The interfaces and types defined here are to facilitate that back-end tracking of MDM commands (by i.e. Command UUID, workflow, and associating any relevant context (if required) to the workflows so that when the device responds to MDM command(s) we can restore that context and "route" the response to the correct workflow for its higher-level handling.

Workflows are identified by names. By convention these are reverse-DNS style and are indended to be unique amongst the workflow engine and be human readable. The workflow names serve as the way to "route" workflow actions to workflows.

Newly started workflows are given an instance ID. This is just a unique identifier for tracking or logging. The intent is to associate this ID to a workflow that has been started and on which devices for logging or other tracking.

Steps

Workflows are facilitated by one or more steps. A step is a set of one or more MDM commands. A newly started workflow enqueues (sends) a step to one or more devices. A step is completed for an enrollment ID when all commands in the step are received by a single device — whether they have an error or not. `NotNow` handling is done for you: a workflow will only receive a response for an `Acknowledge` or `Error` response to an MDM command. The step can Timeout — this is when any of the enqueued commands do not respond within the Timeout given when they were enqueued. Steps are intended to be sequential for an enrollment ID — that is a workflow's step completion handler should only enqueue one step at a time (or none, if the workflow is finished).

Steps are identified by name. There is no convention for these names as they are workflow specific but they should be human readable as they will likely be logged and keyed on. It is intended workflows will identify specific step completions by the name of the step.

Context

When you enqueue a step you can associate a context value with it. This context is marshaled into binary (in any way the workflow may like, but likely to be JSON or a "bare" string). Then, upon step completion, this same context is unmarshaled and handed back to the workflow's step completion handler. In this way a workflow can keep track of any data or metadata between enqueued steps and their responses if you wish. As mentioned above the step itself also has a name which may preclude the need for any additional context, but if you need additional context or data this context is present.

When a workflow is started an initial context can be passed in. Typically this will be from an API handler that takes data in. The step name for a newly started workflow is the empty string.

Process model

No assumptions should be made about the state of the workflow object receiving method calls. In other words assume the worst: that it's a shared object (vs, say, newly instantiated in a request context) and that multiple calls of the methods on the same object will be running concurrently. Protect any shared resources appropriately (e.g. mutexes and locking). Even better is to push any saved state into the storage layers anyway.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrTimeoutNotUsed returned from a workflow Event() method.
	ErrTimeoutNotUsed = errors.New("workflow does not utilize timeouts")

	// ErrStepResultCommandLenMismatch indicates mismatched MDM commands expected.
	// Steps are enqueued with n MDM commands and should only return with
	// that number of commands. This error is for indicating that this
	// was not the case.
	ErrStepResultCommandLenMismatch = errors.New("mismatched number of commands in step result")

	// ErrUnknownStepName occurs when a workflow encounters a step name
	// it does not know about.
	ErrUnknownStepName = errors.New("unknown step name")

	// ErrIncorrectCommandType occurs when a step's expected command is
	// not of the correct type. Workflows should not depend on the ordering
	// of commands in the returned step command slice.
	ErrIncorrectCommandType = errors.New("incorrect command type")

	// ErrIncorrectContextType indicates a step did not receive the
	// correctly instantiated context type for this step name.
	ErrIncorrectContextType = errors.New("incorrect context type")
)
View Source
var ErrEventsNotSupported = errors.New("events not supported for this workflow")

ErrEventsNotSupported returned from a workflow Event() method.

Functions

This section is empty.

Types

type Config

type Config struct {
	// workflow default step timeout.
	// if a workflow does not specify a timeout when enqueueing steps
	// then this default is used. if this default is not sepcified then
	// the engine's default timeout is used.
	Timeout time.Duration

	// defines the workflow exclusivity style
	Exclusivity

	// workflows have the option to receive command responses from
	// any MDM command request type that the engine enqueues (i.e. from
	// other workflows) — not just the commands that this workflow
	// enqueues. specifiy the Request Types for those command here. They
	// will be received by the workflow as an event.
	AllCommandResponseRequestTypes []string

	// event subscriptions. this workflow will get called every time
	// these events happen. use bitwise OR to specify multiple events.
	Events EventFlag
}

Config represents static workflow-wide configuration.

type ContextMarshaler

type ContextMarshaler interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler
}

ContextMarshaler marshals and unmarshals types to and from byte slices. This encapsulates arbitrary context types to be passed around and stored as binary blobs by components (that are not a workflow ) that don't need to care about what the contents are (e.g. storage backends or HTTP handlers).

type Event

type Event struct {
	EventFlag
	// EventData is likely a pointer to a struct of the relevent event data.
	// You will need to know the data you're expecting and use Go type
	// conversion to access it if you need it.
	// For example the EventAuthenticate EventFlag will be
	// a `*mdm.Authenticate` under the `interface{}`.
	EventData interface{}
}

Event is a specific workflow MDM event.

type EventFlag

type EventFlag uint

EventFlag is a bitmask of event types.

const (
	EventAllCommandResponse EventFlag = 1 << iota
	EventAuthenticate
	EventTokenUpdate
	// TokenUpdate and Enrollment are considered distinct because an
	// enrollment will only enroll once, but TokenUpdates can
	// continually arrive.
	EventEnrollment
	EventCheckOut
)

Storage backends (persistent storage) may use these numeric values. So treat these as append-only: order and position matter.

func EventFlagForString

func EventFlagForString(s string) EventFlag

func (EventFlag) String

func (e EventFlag) String() string

func (EventFlag) Valid

func (e EventFlag) Valid() bool

type Exclusivity

type Exclusivity uint

Exclusivity is the exclusivity "mode" for a workflow.

const (
	// Workflow can only run if no other pending step for this workflow
	// for an enrollment id exists in the system.
	// This is the default mode (0 value).
	Exclusive Exclusivity = iota

	// Workflow can run simultaneous instances for an enrollment ID.
	MultipleSimultaneous
)

func (Exclusivity) Valid

func (we Exclusivity) Valid() bool

type IntContext

type IntContext int

IntContext is a simple integer ContextMarshaler.

func (*IntContext) MarshalBinary

func (c *IntContext) MarshalBinary() ([]byte, error)

MarshalBinary converts c into a byte slice.

func (*IntContext) UnmarshalBinary

func (c *IntContext) UnmarshalBinary(data []byte) error

UnmarshalBinary converts and loads data into c.

type MDMContext

type MDMContext struct {
	// Params are the URL parameters included in the MDM request from an
	// enrollment. These parameters would be set on the `CheckInURL` or
	// `ServerURL` parameters in the enrollment profile. Note because
	// these come from a connecting MDM client they may not be present
	// in all contexts — only those that originate from an MDM request.
	Params map[string]string
}

MDMContext contains context related to the MDM server, enrollment, and/or MDM request.

type Namer

type Namer interface {
	// Name returns the name of the workflow; reverse-DNS style by convention.
	// This string is generally used to route actions to this workflow.
	Name() string
}

Namers provide a name string.

type StepContext

type StepContext struct {
	// MDM client/server context. Note that a step can be more than one
	// MDM command response. This means MDMContext will likely only
	// come from the very last command to be seen that completed
	// the step. Previous MDMConext will not be seen/provided.
	MDMContext

	InstanceID string // Unique identifier of the workflow instance

	// Name is used by the workflow to identify which step is being processed.
	// This value can help the workflow differentiate steps for a multi-step workflow.
	// It is also passed to the NewContext() method to determine the data
	// type for unmarshalling. Name is empty when starting a workflow.
	Name string

	// Context is a generic holder of data that workflows will be handed when processing steps.
	// Usually this will be an instance of whatever value the workflow
	// NewContext() method returns for a given step name.
	Context ContextMarshaler
}

StepContext contains context for a step.

func (*StepContext) NewForEnqueue

func (c *StepContext) NewForEnqueue() *StepContext

NewForEnqueue is a helper for creating a new context from c. It copies the instance ID — ostensibly for creating a new Context for the next step enqueueing.

type StepEnqueueing

type StepEnqueueing struct {
	StepContext
	IDs      []string // Enrollment IDs
	Commands []interface{}

	// Timeout specifies a timeout. If any of the commands in this step do
	// not complete by this time then the entire step is considered to have
	// timed out.
	Timeout time.Time

	// A step should not be enqueued (that is, sent to enrollments)
	// until after this time has passed. A delay of sorts.
	NotUntil time.Time
}

StepEnqueueing encapsulates a step and is passed to an enqueuer for command delivery to MDM enrollments. Note that a workflow may only enqueue commands to multiple enrollment IDs when starting.

type StepEnqueuer

type StepEnqueuer interface {
	// EnqueueStep enqueues MDM commands to ids in StepEnqueue.
	// The enqueing system should be able to find this workflow again with Namer.
	EnqueueStep(context.Context, Namer, *StepEnqueueing) error
}

StepEnqueuers send steps (MDM commands) to enrollments.

type StepResult

type StepResult struct {
	StepContext
	ID             string
	CommandResults []interface{}
}

StepResult is given to a workflow when a step has completed or timed out.

func (*StepResult) NewStepEnqueueing

func (step *StepResult) NewStepEnqueueing() *StepEnqueueing

StepEnqueueing preserves some context and IDs from step for enqueueing.

type StepStart

type StepStart struct {
	StepContext
	Event *Event
	IDs   []string // Enrollment IDs
}

StepStart is provided to a workflow when starting a new workflow instance. Note that a workflow may only enqueue commands to multiple enrollment IDs when starting.

func (*StepStart) NewStepEnqueueing

func (step *StepStart) NewStepEnqueueing() *StepEnqueueing

StepEnqueueing preserves some context and IDs from step for enqueueing.

type StringContext

type StringContext string

StringContext is a simple string ContextMarshaler.

func (*StringContext) MarshalBinary

func (c *StringContext) MarshalBinary() ([]byte, error)

MarshalBinary converts c into a byte slice.

func (*StringContext) UnmarshalBinary

func (c *StringContext) UnmarshalBinary(data []byte) error

UnmarshalBinary converts and loads data into c.

type Workflow

type Workflow interface {
	Namer

	// Config returns the workflow configuration.
	Config() *Config

	// NewContextValue returns a newly instantiated context value.
	// This will usally be used by a workflow engine to unmarshal and pass in
	// stored context on a StepContext.
	NewContextValue(stepName string) ContextMarshaler

	// Start starts a new workflow instance for MDM enrollments.
	Start(context.Context, *StepStart) error

	// StepCompleted is the action called when all step MDM commands have reported results.
	// Note that these results may be errors, but NotNow responses are handled for the workflow.
	StepCompleted(context.Context, *StepResult) error

	// StepTimeout occurs when at least one command in a step has failed to complete in time.
	// Timeouts are defined by the step, then any workflow default, then
	// any engine default.
	StepTimeout(context.Context, *StepResult) error

	// Event is called when MDM events happen that are intended for this workflow.
	// A workflow can subscribe to events in its Config struct.
	Event(ctx context.Context, e *Event, id string, mdmCtx *MDMContext) error
}

Workflows send MDM commands and process the results using steps.

Directories

Path Synopsis
Package cmdplan implements a NanoCMD Workflow for sending pre-configured commands to enrollments.
Package cmdplan implements a NanoCMD Workflow for sending pre-configured commands to enrollments.
Package fvenable implements a NanoCMD Workflow for enabling FileVault on a Mac.
Package fvenable implements a NanoCMD Workflow for enabling FileVault on a Mac.
Package fvrotate implements a NanoCMD Workflow for FileVault key rotation.
Package fvrotate implements a NanoCMD Workflow for FileVault key rotation.
Package inventory implements a NanoCMD Workflow that updates an inventory system.
Package inventory implements a NanoCMD Workflow that updates an inventory system.
Pacakge lock implements a DeviceLock PIN escrow workflow.
Pacakge lock implements a DeviceLock PIN escrow workflow.
Package profile implements a NanoCMD Workflow for "statefully" installing and removing profiles.
Package profile implements a NanoCMD Workflow for "statefully" installing and removing profiles.

Jump to

Keyboard shortcuts

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