Documentation
¶
Overview ¶
Package admission is the contract of the dispatch admission hook (ADR 0100): operator code that admits, changes or refuses each task attempt after the server's own checks and before the attempt's agent token is minted.
A hook sees a copy of the attempt (Request) and answers with a Decision: Admit runs it as it is, AdmitWith runs it with the pod placement and metadata changed, Hold keeps it waiting and offers it again, and Refuse fails the task for good with a reason the task instance shows. An error means the hook itself is broken: the attempt is held too, so a hook that is briefly down never lets a task run unchecked, unless the operator chose to fail open.
What a hook may change is limited on purpose: the node selector, tolerations, priority class, service account, and labels and annotations outside the keys Dexaflow reserves. The image, the command, the environment, the namespace and the resources stay as the server chose them, so the attempt is charged the pool slots it was admitted with.
Everything exported here follows the pkg/ compatibility rule of ADR 0099: no breaking change in a patch release.
Example ¶
A hook that runs each tenant's tasks on that tenant's nodes, holds work during a freeze window and refuses tasks that ask for more than 8 CPUs.
package main
import (
"context"
"fmt"
"github.com/dexadata/dexaflow/pkg/admission"
)
func main() {
frozen := func() bool { return true } // ask your change calendar
hook := admission.HookFunc(func(_ context.Context, req *admission.Request) (admission.Decision, error) {
if frozen() {
return admission.Hold("weekly maintenance freeze until 06:00 UTC"), nil
}
if req.Resources.Requests.CPU == "16" {
return admission.Refuse("tasks may request at most 8 CPUs; lower resources.requests.cpu"), nil
}
return admission.AdmitWith(admission.Changes{
NodeSelector: map[string]string{"tenant": req.TenantID},
Tolerations: []admission.Toleration{{Key: "tenant", Operator: "Equal", Value: req.TenantID, Effect: "NoSchedule"}},
Labels: map[string]string{"cost-center": req.TenantID},
}), nil
})
dec, _ := hook.Admit(context.Background(), &admission.Request{TenantID: "acme", DagID: "nightly_etl", TaskID: "extract"})
reason, held := dec.Held()
fmt.Println(held, reason)
}
Output: true weekly maintenance freeze until 06:00 UTC
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsReservedKey ¶
IsReservedKey reports whether a label or annotation key falls under one of ReservedPrefixes.
func ReservedPrefixes ¶
func ReservedPrefixes() []string
ReservedPrefixes returns the label and annotation key prefixes a hook may not set: the server decides what a pod is from keys under them.
Types ¶
type Changes ¶
type Changes struct {
NodeSelector map[string]string
Tolerations []Toleration
// PriorityClassName and ServiceAccount must be allowed by the server's
// execution allowlist (execution_allowlist.priority_classes and
// service_accounts) when those lists are set.
PriorityClassName string
// ServiceAccount must be allowed by the server's execution allowlist
// (execution_allowlist.service_accounts) when that list is set.
ServiceAccount string
// Labels and Annotations may not use a key under one of ReservedPrefixes.
Labels map[string]string
Annotations map[string]string
}
Changes are what a hook may change on the attempt. A zero field changes nothing: map entries are merged over the task's own (the hook's keys win), tolerations are appended, and a non-empty string replaces the task's value.
type Decision ¶
type Decision struct {
// contains filtered or unexported fields
}
Decision is a hook's answer. Its zero value admits the attempt unchanged.
func Hold ¶
Hold keeps the attempt waiting and offers it again later, for a condition that passes (a freeze window, a budget that resets). It is not a failure of the hook: it never fails open and never counts toward skipping the hook. A held attempt waits within the same window as an unreachable cluster (ADR 0093) and fails with reason once that window is spent.
func Refuse ¶
Refuse fails the task for good, without retries, and the task instance shows reason. For a temporary condition use Hold.
type Execution ¶
type Execution struct {
ServiceAccount string
NodeSelector map[string]string
Tolerations []Toleration
PriorityClassName string
Labels map[string]string
Annotations map[string]string
}
Execution is the subset of a task's execution settings a hook sees.
type Hook ¶
Hook admits, changes, holds or refuses one task attempt. The server calls Admit once per attempt, with a deadline on ctx, and stops waiting when the deadline passes. A hook must return once ctx is done: one that ignores it keeps running in the background after the server moved on. Admit must not keep req after it returns.
type Quantity ¶
Quantity holds Kubernetes quantities, for example "500m" or "1Gi". An empty string means not set.
type Request ¶
type Request struct {
// TenantID, DagID, RunID and TaskID name the attempt; TryNumber counts its
// tries from 1.
TenantID string
DagID string
RunID string
TaskID string
TryNumber int
// Operator is the task's operator type, for example "python" or "bash".
Operator string
// Image is the image the run resolved, the one the pod would run.
Image string
// SourceMode reports whether a Cluster attempt runs the DAG source on the
// operator's runtime image (ADR 0067). Lite always runs from source and
// reports false.
SourceMode bool
// Resources are the effective requests and limits: the task's own, or the
// resource unit's or the platform defaults' where the server applies them.
Resources Resources
// Execution is the task's placement and pod metadata, with the service
// account after the server's default is applied.
Execution Execution
// ServiceAccountChosen reports whether the task named its service account
// itself; false means Execution.ServiceAccount is the server's default (or
// empty when no default is configured).
ServiceAccountChosen bool
}
Request describes the attempt about to run. It is a copy: changing it has no effect, a hook answers with a Decision instead. It never carries the task's environment, its source, secrets or the attempt's token.
type Toleration ¶
type Toleration struct {
Key string
Operator string // "Exists" or "Equal"; empty means Equal
Value string
Effect string // "NoSchedule", "PreferNoSchedule", "NoExecute" or empty for all
// TolerationSeconds, for NoExecute only, is how long the pod stays bound
// to a node that gains the taint; nil means forever.
TolerationSeconds *int64
}
Toleration is a Kubernetes pod toleration. Empty fields are left out of the pod.