Documentation
¶
Overview ¶
Package engine implements the export and reconcile logic behind the courier binary: pulling organization controls and mappings out of the Openlane API into controlfile documents, diffing local documents against the live system, and applying creates and updates back through the API
Index ¶
- Constants
- Variables
- func LoadFile(path string) (*Store, []Kind, error)
- type ApplyOptions
- type ApplyResult
- type Change
- type Client
- type Config
- type FormatResult
- type Kind
- type Option
- type PullResult
- type RemoteControl
- type RemoteMapping
- type RemotePolicy
- type RemoteRef
- type RemoteState
- type RemoteSubcontrol
- type Settings
- type Store
Constants ¶
const ( // DefaultHost is the Openlane API host used when none is configured DefaultHost = "https://api.theopenlane.io" // DefaultDir is the store directory used when none is configured DefaultDir = "./data" )
const DefaultConfigFile = "./config/.config.yaml"
DefaultConfigFile is the config file loaded from the working directory when no explicit path is given
Variables ¶
var ( // ErrMissingToken is returned when no API token is configured ErrMissingToken = errors.New("missing API token, set COURIER_TOKEN, add token to config/.config.yaml, or pass --token") // ErrMissingHost is returned when no API host is configured ErrMissingHost = errors.New("missing API host, set COURIER_HOST, add host to config/.config.yaml, or pass --host") // ErrMultipleControlsFound is returned when a mapped control refCode matches more than one control ErrMultipleControlsFound = errors.New("multiple controls found for refCode") // ErrMissingMarkdown is returned when a policy manifest entry references a markdown file that does not exist ErrMissingMarkdown = errors.New("policy markdown file not found") // ErrUnrecognizedFile is returned when a file does not validate as a control inventory or a policy manifest ErrUnrecognizedFile = errors.New("file is not a recognizable control inventory or policy manifest") // ErrPaginationStalled is returned when the API reports another page but does not advance the cursor ErrPaginationStalled = errors.New("pagination stalled, the API reported another page without advancing the cursor") )
Functions ¶
Types ¶
type ApplyOptions ¶ added in v0.0.2
type ApplyOptions struct {
// DryRun reports what apply would change without writing anything
DryRun bool
}
ApplyOptions configures an apply run
type ApplyResult ¶
type ApplyResult struct {
// CreatedControls are the controls created, in order
CreatedControls []Change `json:"createdControls,omitempty"`
// UpdatedControls are the controls updated, in order, with the fields that differ
UpdatedControls []Change `json:"updatedControls,omitempty"`
// UnchangedControls counts controls that already match Openlane
UnchangedControls int `json:"unchangedControls"`
// CreatedMappings are the mappings created, in order, with the targets added
CreatedMappings []Change `json:"createdMappings,omitempty"`
// UpdatedMappings are the mappings courier already owned and extended
UpdatedMappings []Change `json:"updatedMappings,omitempty"`
// CreatedPolicies are the policies created, in order
CreatedPolicies []Change `json:"createdPolicies,omitempty"`
// UpdatedPolicies are the policies updated, in order, with the fields that differ
UpdatedPolicies []Change `json:"updatedPolicies,omitempty"`
// UnchangedPolicies counts policies that already match Openlane
UnchangedPolicies int `json:"unchangedPolicies"`
// Warnings are non-fatal issues such as mapped control refCodes that
// could not be resolved and were skipped
Warnings []string `json:"warnings,omitempty"`
// Errors are per-record failures, the run continues past them so one
// rejected record does not abort the batch
Errors []string `json:"errors,omitempty"`
}
ApplyResult summarizes what an apply changed in the API
type Change ¶ added in v0.0.2
type Change struct {
// Ref is the control refCode or the policy name
Ref string `json:"ref"`
// Detail is the managed fields that differ, or the mapping targets added,
// empty when the record is created outright
Detail []string `json:"detail,omitempty"`
}
Change is one record apply wrote, or would write on a dry run
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps the generated Openlane GraphQL client
func (*Client) Apply ¶
func (c *Client) Apply(ctx context.Context, store *Store, kinds []Kind, opts ApplyOptions) (*ApplyResult, error)
Apply pushes the store files through the API for the selected kinds in registry order. Remote state is fetched first and every record is compared against it, so only entries that actually differ are written and a repeated apply is a no-op. With DryRun the same comparison runs and the result reports what would change without writing anything. Nothing is ever deleted
func (*Client) FetchState ¶
FetchState pulls the remote state for the selected kinds from the API
type Config ¶
type Config struct {
// Host is the base URL of the Openlane API
Host string
// Token is the API token used for authentication
Token string
// OrganizationID optionally scopes requests to a specific organization
OrganizationID string
}
Config holds the connection settings for the Openlane API
type FormatResult ¶
type FormatResult struct {
// Changed are the relative paths whose content differs from canonical form
Changed []string
}
FormatResult reports what Format changed or would change
type Kind ¶ added in v0.0.2
type Kind string
Kind identifies a syncable object kind
func AllKinds ¶ added in v0.0.2
func AllKinds() []Kind
AllKinds lists every registered kind in apply order
func SelectKinds ¶ added in v0.0.2
SelectKinds resolves per-kind flag selections into the kinds to operate on, no selection means every kind, order always follows the registry
type Option ¶ added in v0.0.2
type Option func(*Client)
Option configures a Client
func WithGraphClient ¶ added in v0.0.2
func WithGraphClient(typed graphclient.GraphClient) Option
WithGraphClient supplies the GraphQL client instead of building one from the config, so the pull, plan, and apply paths can be driven against a fake
type PullResult ¶
type PullResult struct {
// TotalControls is the number of organization controls exported
TotalControls int `json:"totalControls"`
// TotalPolicies is the number of internal policies exported
TotalPolicies int `json:"totalPolicies"`
// Written are the relative paths created or updated
Written []string `json:"written,omitempty"`
// Removed are the relative paths of stale documents deleted
Removed []string `json:"removed,omitempty"`
// Warnings are records the export could not represent, such as a
// subcontrol whose parent control is not itself exported
Warnings []string `json:"warnings,omitempty"`
}
PullResult summarizes what a pull wrote to the store
type RemoteControl ¶
type RemoteControl struct {
// ID is the Openlane ULID of the control
ID string `json:"id"`
// RefCode is the unique reference code of the control
RefCode string `json:"refCode"`
// Title is the human readable title of the control
Title string `json:"title"`
// Description describes what the control is supposed to accomplish
Description string `json:"description"`
// Category is the category of the control
Category string `json:"category"`
// Subcategory is the subcategory of the control
Subcategory string `json:"subcategory"`
// ReferenceFramework is the framework short name when the control derives from a standard
ReferenceFramework string `json:"referenceFramework"`
// Tags associated with the control
Tags []string `json:"tags"`
}
RemoteControl is the authorable view of a control as it exists in the API
type RemoteMapping ¶
type RemoteMapping struct {
// ID is the Openlane ULID of the mapped control record
ID string `json:"id"`
// Source is how the mapping was created, courier owns the imported ones
Source string `json:"source"`
// From are the controls and subcontrols on the from side of the mapping
From []RemoteRef `json:"from"`
// To are the controls and subcontrols on the to side of the mapping
To []RemoteRef `json:"to"`
}
RemoteMapping is a mapped-control record as it exists in the API, the participant edges derive mappedControls lists and the source identifies the records courier owns and may edit in place
type RemotePolicy ¶
type RemotePolicy struct {
// ID is the Openlane ULID of the policy
ID string `json:"id"`
// Name is the unique name of the policy
Name string `json:"name"`
// KindName is the policy kind, e.g. Security, Operational
KindName *string `json:"internalPolicyKindName"`
// Status is the document status, e.g. PUBLISHED, DRAFT
Status string `json:"status"`
// Revision is the document revision, e.g. v1.0.0
Revision string `json:"revision"`
// Details is the stored policy body
Details *string `json:"details"`
// Tags associated with the policy
Tags []string `json:"tags"`
// Controls are the controls linked to the policy
Controls []RemoteRef `json:"controls"`
}
RemotePolicy is an internal policy as it exists in the API
type RemoteRef ¶
type RemoteRef struct {
// ID is the Openlane ULID of the referenced record
ID string `json:"id"`
// RefCode is the reference code of the referenced record
RefCode string `json:"refCode"`
// Framework is the short name of the framework the record derives from,
// empty for organization custom controls
Framework string `json:"framework"`
// Subcontrol reports whether the reference is a subcontrol rather than a
// control, the two live in separate edge sets on a mapping
Subcontrol bool `json:"subcontrol,omitempty"`
}
RemoteRef identifies a control or subcontrol participating in a mapping or policy edge
type RemoteState ¶
type RemoteState struct {
// Controls are the organization-owned controls not derived from a framework
Controls []RemoteControl
// Subcontrols are the organization-owned subcontrols of those controls
Subcontrols []RemoteSubcontrol
// Mappings are the organization-owned mapped-control records
Mappings []RemoteMapping
// Policies are the organization-owned internal policies
Policies []RemotePolicy
}
RemoteState is the full set of organization controls, mappings, and policies pulled from the API
type RemoteSubcontrol ¶ added in v0.0.2
type RemoteSubcontrol struct {
// RemoteControl holds the fields a subcontrol shares with a control
RemoteControl
// ControlID is the Openlane ULID of the parent control
ControlID string `json:"controlID"`
}
RemoteSubcontrol is a subcontrol as it exists in the API, it carries the same authorable fields as a control plus the control it belongs to
type Settings ¶
type Settings struct {
// Host is the base URL of the Openlane API
Host string `koanf:"host"`
// Token is the API token used for authentication
Token string `koanf:"token" sensitive:"true"`
// OrganizationID optionally scopes requests to a specific organization, only needed for multi-organization tokens
OrganizationID string `koanf:"organization-id"`
// Dir is the directory holding the exported files
Dir string `koanf:"dir"`
}
Settings are the connection and store settings for the binary
func DefaultSettings ¶ added in v0.0.2
func DefaultSettings() Settings
DefaultSettings are the settings used before any source is merged in
func LoadSettings ¶
LoadSettings merges settings from the config file at path (DefaultConfigFile when empty), COURIER_-prefixed environment variables, and set flags, later sources win. A missing default config file is not an error, a missing explicit one is
type Store ¶ added in v0.0.2
type Store struct {
// Dir is the directory the files were loaded from
Dir string
// Controls is the parsed control inventory
Controls []*controlfile.Control
// Policies is the parsed policy manifest
Policies []*controlfile.Policy
// PolicyMarkdown holds the raw markdown document per manifest entry
PolicyMarkdown map[string][]byte
}
Store is the parsed content of an export directory: the control inventory, the policy manifest, and the raw policy markdown documents keyed by store-relative path