spec

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package spec defines the public task specification: the YAML/JSON document an operator writes and the validated Go type every Podium component codes against.

Index

Constants

View Source
const (
	DefaultWorkingDir       = "/workspace"
	DefaultTimeout          = time.Hour
	DefaultMaxAttempts      = 1
	DefaultPIDs             = 4096
	DefaultReadinessTimeout = time.Minute
	DefaultHTTPPort         = 80
)

Defaults applied by ApplyDefaults.

View Source
const (
	// SecretTargetEnv puts the value in an environment variable named by Key.
	SecretTargetEnv = "env"
	// SecretTargetFile mounts the value read-only at the absolute path named by Key.
	SecretTargetFile = "file"
)

Secret targets. A ref says where in the container the value should appear, never what the value is.

View Source
const DefaultSecretTarget = SecretTargetEnv

DefaultSecretTarget is what ApplyDefaults fills an unset target with.

View Source
const ReservedSidecarName = "task"

ReservedSidecarName is the network alias the task container itself answers to, so no sidecar may take it.

View Source
const SecretsMount = "/podium/secrets"

SecretsMount is the tmpfs every task container gets for secret files. A file target outside it works, but this is the path the docs use and the only one guaranteed to be writable, non-executable and gone when the container is.

View Source
const SpecDocs = "docs/task-spec.md"

SpecDocs is where a validation error points a reader who wants the whole picture.

Variables

View Source
var AllowedCapabilities = []string{
	"CHOWN", "DAC_OVERRIDE", "FOWNER", "SETUID", "SETGID", "NET_BIND_SERVICE", "KILL",
}

AllowedCapabilities is the complete set of Linux capabilities a task may add back after the executor drops them all. Anything outside it is rejected at validation: a task that needs more is a node-level operator decision, not a spec field.

View Source
var RegistryHostRE = regexp.MustCompile(`^[a-z0-9]([a-z0-9.-]*[a-z0-9])?(:[0-9]{1,5})?$`)

RegistryHostRE is what a registry host may look like once normalised: a hostname, with an optional port. It is what an image reference's domain part is.

View Source
var SecretNameRE = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_.-]*$`)

SecretNameRE is the shape of a secret name. It is deliberately narrow: a name reaches the node as a filename on the way to a file-target mount, and it is what appears in a redaction marker in stored logs.

Functions

func NormalizeCapability

func NormalizeCapability(c string) string

NormalizeCapability is the spelling the executor passes to Docker: uppercase, with the optional CAP_ prefix stripped, so both "chown" and "CAP_CHOWN" name the same capability.

func NormalizeRegistryHost

func NormalizeRegistryHost(host string) string

NormalizeRegistryHost reduces the ways people spell a registry to the domain an image reference carries: lower-case, no scheme, no path, and Docker Hub's legacy index address folded into "docker.io".

func RegistryHost

func RegistryHost(image string) (string, error)

RegistryHost is the registry an image reference is pulled from, normalised the same way: "alpine:3" is docker.io, "us-docker.pkg.dev/acme/images/app" is us-docker.pkg.dev.

func ValidateRegistryHost

func ValidateRegistryHost(host string) (string, error)

ValidateRegistryHost normalises host and rejects anything that could not be the domain of an image reference.

Types

type Duration

type Duration time.Duration

Duration is a time.Duration that marshals to and from a Go duration string ("30s", "1h") in both YAML and JSON.

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON encodes the duration as a string.

func (Duration) MarshalYAML

func (d Duration) MarshalYAML() (any, error)

MarshalYAML encodes the duration as a string.

func (Duration) Std

func (d Duration) Std() time.Duration

Std returns the underlying time.Duration.

func (Duration) String

func (d Duration) String() string

String renders the duration the way time.Duration does.

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(b []byte) error

UnmarshalJSON accepts a duration string ("30s") or a raw nanosecond count.

func (*Duration) UnmarshalYAML

func (d *Duration) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts a duration string ("30s").

type Hardening

type Hardening struct {
	ReadOnlyRootfs bool     `yaml:"read_only_rootfs,omitempty" json:"read_only_rootfs,omitempty"`
	Capabilities   []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
}

Hardening relaxes or tightens the task container's sandbox. The defaults — every capability dropped, no new privileges, the engine's seccomp profile — are not negotiable; these two fields are the only dials.

type Readiness

type Readiness struct {
	TCPPort  int      `yaml:"tcp_port,omitempty" json:"tcp_port,omitempty"`
	HTTPPath string   `yaml:"http_path,omitempty" json:"http_path,omitempty"`
	HTTPPort int      `yaml:"http_port,omitempty" json:"http_port,omitempty"`
	Command  []string `yaml:"command,omitempty" json:"command,omitempty"`
	Timeout  Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
}

Readiness is how the node decides a sidecar is usable. At most one probe may be set; a sidecar with none is ready as soon as its container is running.

func (Readiness) Probes

func (r Readiness) Probes() int

Probes reports how many readiness probes are configured. Validation allows 0 or 1.

type Resources

type Resources struct {
	CPU      float64 `yaml:"cpu,omitempty" json:"cpu,omitempty"`
	MemoryMB int     `yaml:"memory_mb,omitempty" json:"memory_mb,omitempty"`
	PIDs     int     `yaml:"pids,omitempty" json:"pids,omitempty"`
}

Resources caps one container. The node applies them blindly; refusing a task that asks for more than a node has is the scheduler's job.

type SecretRef

type SecretRef struct {
	Name   string `yaml:"name" json:"name"`
	Target string `yaml:"target,omitempty" json:"target,omitempty"`
	Key    string `yaml:"key" json:"key"`
}

SecretRef names a stored secret and says where the task wants it. It carries the name of a value, never the value: the server resolves it immediately before assignment.

type Sidecar

type Sidecar struct {
	Image     string            `yaml:"image" json:"image"`
	Command   []string          `yaml:"command,omitempty" json:"command,omitempty"`
	Env       map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
	Readiness Readiness         `yaml:"readiness,omitempty" json:"readiness,omitempty"`
	Resources Resources         `yaml:"resources,omitempty" json:"resources,omitempty"`
	// Privileged runs the sidecar with every capability and no device restriction, which
	// is root on the node's kernel. It exists for one thing — a Docker daemon beside the
	// task, so a turn can run `docker compose` and testcontainers — and the spec only
	// *asks*: a node started without --allow-privileged-sidecars refuses the task at
	// provisioning. Validation lets it through on purpose, because whether a machine will
	// host such a container is an operator's answer and not a parser's.
	Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty"`
	// ShareWorkspace mounts the task's workspace volume in the sidecar at the same path
	// the task sees it. A nested daemon resolves a bind-mount source in its OWN
	// filesystem, so a `docker run -v /workspace/...` or a build context under /workspace
	// only works when the daemon sees that path too.
	ShareWorkspace bool `yaml:"share_workspace,omitempty" json:"share_workspace,omitempty"`
}

Sidecar is a sibling container started before the task and reachable from it by the name it is keyed under, which becomes its DNS alias on the task network.

type TaskSpec

type TaskSpec struct {
	Image       string             `yaml:"image" json:"image"`
	Command     []string           `yaml:"command,omitempty" json:"command,omitempty"`
	WorkingDir  string             `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
	Env         map[string]string  `yaml:"env,omitempty" json:"env,omitempty"`
	Secrets     []SecretRef        `yaml:"secrets,omitempty" json:"secrets,omitempty"`
	Sidecars    map[string]Sidecar `yaml:"sidecars,omitempty" json:"sidecars,omitempty"`
	Resources   Resources          `yaml:"resources,omitempty" json:"resources,omitempty"`
	Hardening   Hardening          `yaml:"hardening,omitempty" json:"hardening,omitempty"`
	Labels      []string           `yaml:"labels,omitempty" json:"labels,omitempty"`
	Timeout     Duration           `yaml:"timeout,omitempty" json:"timeout,omitempty"`
	MaxAttempts int                `yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"`
	// RetryOnNodeLoss requeues the task as a new attempt when the node running it goes
	// offline mid-run, instead of marking it lost. It is off by default because a task
	// that is not idempotent must not be silently run twice.
	RetryOnNodeLoss bool `yaml:"retry_on_node_loss,omitempty" json:"retry_on_node_loss,omitempty"`
}

TaskSpec is what a task runs. It mirrors podium.v1.TaskSpec on the wire.

func FromProto

func FromProto(p *podiumv1.TaskSpec) *TaskSpec

FromProto converts a wire TaskSpec back to the public type.

func ParseTaskSpec

func ParseTaskSpec(r io.Reader) (*TaskSpec, error)

ParseTaskSpec decodes a YAML task spec, applies defaults and validates it.

func (*TaskSpec) ApplyDefaults

func (s *TaskSpec) ApplyDefaults()

ApplyDefaults fills unset fields. It never overwrites a value the caller set, so an invalid one (a negative timeout, say) survives for Validate to reject.

func (*TaskSpec) Images

func (s *TaskSpec) Images() []string

Images is every image reference a spec pulls: the task's own and each sidecar's.

func (*TaskSpec) ToProto

func (s *TaskSpec) ToProto() *podiumv1.TaskSpec

ToProto converts a TaskSpec to its wire form.

func (*TaskSpec) Validate

func (s *TaskSpec) Validate() error

Validate reports every problem with the spec at once.

Jump to

Keyboard shortcuts

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