package model import ( "errors" "time" ) // Sentinel errors for jobs. var ( // ErrJobActive means the service already has a queued or running job. // CreateJob and jobs.Service.Trigger return it together with that job. ErrJobActive = errors.New("an update is already in progress for this service") // ErrAgentOffline means no connected agent can run the job. ErrAgentOffline = errors.New("agent offline") // ErrAgentTooOld means the only agent that could run the job speaks a // protocol version without jobs. ErrAgentTooOld = errors.New("agent too old for updates") // ErrAgentTooOldForPinned means the only agent that could run the job // speaks protocol version 2, which cannot deploy a digest-pinned or // tagless ref. errors.Is(err, ErrAgentTooOld) is true for it as well. ErrAgentTooOldForPinned error = agentTooOldForPinned{} ) type agentTooOldForPinned struct{} func (agentTooOldForPinned) Error() string { return "agent too old to deploy a pinned version (needs protocol 3)" } func (agentTooOldForPinned) Is(target error) bool { return target == ErrAgentTooOld } // NotUpdatableError says why a service cannot be updated. Reason is shown // in the UI as is. type NotUpdatableError struct { Reason string } func (e *NotUpdatableError) Error() string { return "not updatable: " + e.Reason } // Reasons a service cannot be updated. const ( ReasonSelf = "updated manually outside the system" ReasonContainer = "plain containers cannot be updated" ReasonNoTag = "follows no tag" ReasonRemoved = "service no longer exists" ReasonBuilt = "built from source (build:)" ReasonDrifted = "drifted: restore or accept the running version first" ReasonNotDrifted = "runs its desired version" ) // MergeRefusedError says why two services cannot be merged. Reason is shown // in the UI as is. type MergeRefusedError struct { Reason string } func (e *MergeRefusedError) Error() string { return "cannot merge: " + e.Reason } // Reasons two services cannot be merged. ReasonSelf is reused for the // updater itself. const ( ReasonMergeSame = "a service cannot be merged into itself" ReasonMergeLiveOld = "the old service still exists" ReasonMergeGoneNew = "the new service no longer exists" ReasonMergeKind = "only services of the same kind can be merged" ReasonMergeContainer = "plain containers cannot be merged" ) // JobStatus is the state of an update job. type JobStatus string // Job statuses. A job moves queued -> running -> succeeded|failed, or // straight from queued to failed. const ( JobQueued JobStatus = "queued" JobRunning JobStatus = "running" JobSucceeded JobStatus = "succeeded" JobFailed JobStatus = "failed" ) // JobTrigger says what created a job. TriggerManual comes from the UI, TriggerWebhook from POST /update. type JobTrigger string // Job triggers. const ( TriggerManual JobTrigger = "manual" TriggerWebhook JobTrigger = "webhook" TriggerPoll JobTrigger = "poll" ) // Job is one update of one service. type Job struct { ID int64 ServiceID int64 ServiceName string // Service.DisplayName() of the job's service AgentID *int64 // agent the job was sent to; nil if it never was AgentName string FromRef string ToRef string // "repo:tag" when created; "repo:tag@digest" after success Trigger JobTrigger RequestedBy string // OIDC subject for manual jobs Status JobStatus Output string // empty in ListJobs results OutputTruncated bool Error string CreatedAt time.Time StartedAt *time.Time FinishedAt *time.Time } // Active reports whether the job is queued or running. func (j Job) Active() bool { return j.Status == JobQueued || j.Status == JobRunning } // NewJob is what CreateJob needs to insert a queued job. type NewJob struct { ServiceID int64 AgentID *int64 FromRef string ToRef string Trigger JobTrigger RequestedBy string CreatedAt time.Time } // JobFinish is a job's outcome as reported by the agent. type JobFinish struct { Status JobStatus // JobSucceeded or JobFailed Ref string // deployed "repo:tag@digest"; set on success Error string Output string OutputTruncated bool StartedAt time.Time // agent clock FinishedAt time.Time // agent clock } // JobFilter selects jobs for ListJobs. ServiceID 0 means all services. // Limit 0 means 100. type JobFilter struct { ServiceID int64 Limit int } // Route is the hub's choice of agent connection for a job. ConnID identifies // the connection, so a job is never sent over a connection that replaced the // one chosen. type Route struct { AgentID int64 ConnID uint64 }