application

package
v1.6.5 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: AGPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package application manages applications, their config, and deployments.

Index

Constants

View Source
const MaxReplicas = 100

MaxReplicas caps a service app's replica count so a single request can't ask Swarm to schedule an unbounded number of tasks (node resource-exhaustion DoS).

Variables

View Source
var (
	ErrSlugTaken          = errors.New("application name already taken")
	ErrNameInvalid        = errors.New("name must contain only lowercase letters, digits and hyphens")
	ErrImageRequired      = errors.New("image is required for image-source applications")
	ErrGitRepoRequired    = errors.New("git_repo is required for git-source applications")
	ErrBuildConfigOnImage = errors.New("build configuration is only valid for git-source applications")
	ErrInvalidBuildMethod = errors.New("invalid build method")
	ErrNotDeployable      = errors.New("application has no active release")
	ErrVolumeNotFound     = errors.New("volume not found in workspace")
	ErrEnvVarNotFound     = errors.New("environment variable not found")
	ErrMountPathRequired  = errors.New("mount path is required")
	ErrReleaseNotFound    = errors.New("release not found")
	ErrReleaseActive      = errors.New("cannot delete the active release")
	ErrReleasePinned      = errors.New("cannot delete a pinned release")
	ErrStackNotFound      = errors.New("stack not found in workspace")
	ErrAppRunning         = errors.New("stop the application before deleting it")
	ErrNodeMismatch       = errors.New("the volume is on a different node than this application")

	ErrUnknownHostPreset      = errors.New("unknown host mount preset")
	ErrHostMountNotPrivileged = errors.New("host mounts require a privileged workspace")

	ErrInvalidGPUCount = errors.New("gpu_count must be between 0 and 64")

	ErrClusterDisabled       = errors.New("cluster mode is not enabled; cannot run this application as a service")
	ErrNotService            = errors.New("this operation is only valid for service-runtime (cluster) applications")
	ErrLocalVolumeReplicated = errors.New("a replicated (replicas>1) service cannot mount a node-local volume; use a shared (nfs/cifs) volume or set replicas to 1")
	ErrHostBindService       = errors.New("a service-runtime app cannot use a privileged host mount; host paths are node-local and don't follow a rescheduled task — use the container runtime or a managed volume")
	ErrTooManyReplicas       = fmt.Errorf("replica count exceeds the maximum of %d", MaxReplicas)
	ErrPortRange             = errors.New("container port must be between 1 and 65535")
)
View Source
var (
	// ErrCustomLabelsDisabled is returned when the fleet-wide kill-switch is off.
	ErrCustomLabelsDisabled = &labelError{code: "FEATURE_DISABLED", msg: "custom container labels are disabled by the administrator"}
	// ErrLabelReserved is returned when a user tries to set a platform-reserved key.
	ErrLabelReserved = &labelError{code: "LABEL_RESERVED", msg: "label key is reserved by the platform (io.miabi.*, com.docker.*)"}
	// ErrTooManyLabels / ErrLabelInvalid guard size and shape.
	ErrTooManyLabels = &labelError{code: "LABELS_TOO_MANY", msg: fmt.Sprintf("too many container labels (max %d)", maxContainerLabels)}
	ErrLabelInvalid  = &labelError{code: "LABEL_INVALID", msg: "label key must be non-empty and within length limits"}
)
View Source
var (
	ErrNoCanary     = errors.New("no canary deployment in progress")
	ErrCanaryActive = errors.New("a canary deployment is already in progress")
)
View Source
var ErrNoActiveContainer = errors.New("application has no active container")

ErrNoActiveContainer is returned by Exec when the app has no running container to attach a shell to.

View Source
var ErrResourceCap = errors.New("requested resources exceed the platform limit")

ErrResourceCap is returned when a requested CPU/memory exceeds a platform cap.

View Source
var ErrTaskOnUnmanagedNode = errors.New(
	"this app's task runs on a swarm node with no Miabi agent, so a shell cannot be opened. " +
		"Add the node to Miabi (install the agent) to use exec")

ErrTaskOnUnmanagedNode is the user-facing form of nodes.ErrTaskUnreachable: the app IS running, but on a swarm node with no Miabi agent, so there is no engine to open a shell or read processes through. Docker offers no manager-side equivalent of exec, so this is a hard limit, not a bug. Logs are unaffected — the manager aggregates those.

Functions

This section is empty.

Types

type ClusterCap

type ClusterCap interface {
	CapCluster() bool
}

ClusterCap reports whether the manager is a swarm manager (cluster mode on). Implemented by the cluster service; used to gate "service" runtime apps.

type CreateInput

type CreateInput struct {
	// DisplayName is the free-text label. Handle is the desired unique slug
	// (the URL/CLI handle); when blank it is derived from DisplayName.
	DisplayName string
	Handle      string
	ServerID    uint // node to place on (0 = local)
	SourceType  models.AppSourceType
	Icon        string // optional logo (URL or "mdi-…" class); set from a template
	Image       string
	Tag         string
	GitRepo     string
	GitRef      string
	// Build config (git source only). BuildMethod defaults to auto; Builder,
	// Buildpacks, and BuildEnv tune a buildpack build. Rejected for image apps.
	BuildMethod     models.AppBuildMethod
	Builder         string
	Buildpacks      []string
	BuildEnv        map[string]string
	RegistryID      *uint
	GitRepositoryID *uint
	StackID         *uint
	NetworkIDs      []uint
	Ports           []PortSpec
	Command         []string
	Port            int
	MemoryBytes     int64
	NanoCPUs        int64
	// GPUCount / GPUKind request whole GPU devices. Gated by the AllowGPU plan
	// capability at create time; 0 = none.
	GPUCount        int
	GPUKind         string
	RestartPolicy   models.RestartPolicy
	ImagePullPolicy models.ImagePullPolicy
	// Cluster runtime (cluster mode). RuntimeKind defaults to container; service
	// runs the app as a replicated Swarm service (rejected when cluster mode is
	// off). Replicas/PlacementConstraints/UpdateConfig apply to the service runtime.
	RuntimeKind          models.RuntimeKind
	Replicas             int
	PlacementConstraints []string
	UpdateConfig         *models.ServiceUpdateConfig
	// Metadata is the app's initial metadata. Callers may set reserved
	// "miabi.io/" keys (e.g. provenance); a missing managed-by defaults to
	// "user". HTTP handlers must sanitize user input before setting reserved keys.
	Metadata models.Metadata
	// Annotations is the app's initial annotations: free-form descriptive notes
	// with no reserved keys (the manifest's metadata.annotations).
	Annotations models.Metadata
	// ContainerLabels are user-defined Docker labels for the app's container(s)
	// (Traefik &c.). Reserved keys (io.miabi.*, com.docker.*) are sanitized on
	// create regardless of caller.
	ContainerLabels map[string]string
}

CreateInput describes a new application.

type LiveStatus

type LiveStatus struct {
	Status         string              `json:"status"` // running|restarting|unhealthy|starting|exited|stopped|paused|created|no_container
	ContainerState string              `json:"container_state,omitempty"`
	Health         string              `json:"health,omitempty"`
	Running        bool                `json:"running"`
	Restarting     bool                `json:"restarting"`
	RestartCount   int                 `json:"restart_count"`
	ExitCode       int                 `json:"exit_code"`
	StartedAt      string              `json:"started_at,omitempty"`
	UptimeSeconds  int64               `json:"uptime_seconds"`
	HasContainer   bool                `json:"has_container"`
	StoredStatus   models.AppStatus    `json:"stored_status"`
	Stats          *docker.StatsSample `json:"stats,omitempty"`
	// Networks lists the running container's in-network IPs (ephemeral).
	Networks []docker.ContainerNetwork `json:"networks,omitempty"`
	// Cluster (service) runtime: the desired replica count and how many tasks are
	// currently running. Zero/omitted for container apps.
	ServiceReplicas     uint64 `json:"service_replicas,omitempty"`
	ServiceRunningTasks uint64 `json:"service_running_tasks,omitempty"`
}

LiveStatus is the real-time status of an application's container, derived from Docker inspect (not the stored, deploy-time status). It also carries a live resource-usage snapshot when the container is running.

type NetworkEnsurer

type NetworkEnsurer interface {
	EnsureDefault(ctx context.Context, workspaceID uint) (*models.Network, error)
}

NetworkEnsurer resolves — creating if necessary — a workspace's default, platform-managed Docker network, so every app is guaranteed to join it even when the network was not provisioned at workspace-creation time (e.g. the Docker daemon was briefly unavailable then). Implemented by the network service; an interface keeps application decoupled from it.

type NodeDocker

type NodeDocker interface {
	For(serverID uint) (docker.Client, error)
	LocalID() uint
	// ForServiceTask finds the engine holding a swarm service's task container.
	// A service has no fixed node, and only the node running the task can see it.
	ForServiceTask(ctx context.Context, serviceName string) (docker.Client, string, error)
}

NodeDocker resolves the Docker client for a node id (0 = local).

type NodeGuard

type NodeGuard interface {
	Placeable(serverID uint) error
}

NodeGuard validates that a node can accept a new placement (exists, not cordoned). Implemented by the node service; injected after construction.

type NodeNamer

type NodeNamer interface {
	NameBySwarmNodeID(swarmNodeID string) string
}

NodeNamer resolves a swarm node id to its Miabi display name, so a cluster app's real replica placement can be shown by node name. Implemented by the node service; injected after construction (optional — placement degrades to a short swarm id when unset).

type Overview

type Overview struct {
	Status         models.AppStatus     `json:"status"`
	SourceType     models.AppSourceType `json:"source_type"`
	Image          string               `json:"image,omitempty"`
	Tag            string               `json:"tag,omitempty"`
	GitRepo        string               `json:"git_repo,omitempty"`
	CurrentVersion int                  `json:"current_version"` // 0 = no active release
	CurrentImage   string               `json:"current_image,omitempty"`
	// Hostname is the app's stable internal DNS name, reachable from other
	// managed containers on the shared network across redeploys. StackHostname
	// is its service name within its stack network (empty when ungrouped).
	Hostname         string            `json:"hostname"`
	StackHostname    string            `json:"stack_hostname,omitempty"`
	RedeployRequired bool              `json:"redeploy_required"`
	VolumesCount     int               `json:"volumes_count"`
	RoutesCount      int               `json:"routes_count"`
	NetworksCount    int               `json:"networks_count"`
	EnvCount         int               `json:"env_count"`
	CreatedAt        time.Time         `json:"created_at"`
	RecentEvents     []models.AppEvent `json:"recent_events"`
}

Overview is an aggregated summary of an application for the detail page.

type PortSpec

type PortSpec struct {
	ContainerPort int
	Protocol      string
	Scheme        string
	Name          string
}

PortSpec describes a container port to declare on an application.

type RouteSyncer

type RouteSyncer interface {
	SyncRoute(ctx context.Context, appID uint) error
	// RemoveAppRoutes deletes all of an app's routes (generated + user) from the
	// database and the proxy, used when the app itself is deleted.
	RemoveAppRoutes(ctx context.Context, appID uint) error
}

RouteSyncer reconciles an application's reverse-proxy routes. The route service implements it; injected after construction to avoid an import cycle.

type ServerInfo

type ServerInfo interface {
	Get(id uint) (*models.Server, error)
}

ServerInfo resolves a node's display metadata by id. Implemented by the node service; injected after construction (optional — read paths degrade to an empty server name when unset).

type Service

type Service struct {
	// contains filtered or unexported fields
}

func (*Service) AbortCanary

func (s *Service) AbortCanary(ctx context.Context, app *models.Application) error

AbortCanary stops the canary container, discards its release, and returns all traffic to the stable release.

func (*Service) AppsReferencingSecret

func (s *Service) AppsReferencingSecret(workspaceID uint, name string) ([]models.Application, error)

AppsReferencingSecret returns the workspace's apps whose env references the named secret (`${{ secrets.NAME }}`). Used by the Vault for "used by", the delete guard, and rotation fan-out. Satisfies secret.Consumers.

func (*Service) AttachHostMount

func (s *Service) AttachHostMount(app *models.Application, preset, path string, readOnly bool) error

AttachHostMount attaches an allow-listed privileged host bind (see package hostmount) to the app at path (preset default target when empty). Requires the app's workspace to be privileged; the host source path is resolved from the preset at deploy time and is never taken from the client. Takes effect on the next deploy.

func (*Service) AttachVolume

func (s *Service) AttachVolume(app *models.Application, volumeID uint, path string) error

AttachVolume mounts a workspace volume into the app at path. Takes effect on the next deploy.

func (*Service) AutoRedeploy

func (s *Service) AutoRedeploy(app *models.Application) (*models.Deployment, error)

func (*Service) Create

func (s *Service) Create(workspaceID uint, in CreateInput) (*models.Application, error)

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, app *models.Application) error

Delete removes the application and its active container. Refuses while the app is running — it must be stopped first, to avoid yanking a live workload.

func (*Service) DeleteEnvVar

func (s *Service) DeleteEnvVar(appID uint, key string) error

func (*Service) DeleteRelease

func (s *Service) DeleteRelease(ctx context.Context, app *models.Application, releaseID uint) error

DeleteRelease removes a non-active, non-pinned release and its retired container (best-effort).

func (*Service) Deploy

func (s *Service) Deploy(app *models.Application, registryOverride *uint, tagOverride string, strategy models.DeployStrategy) (*models.Deployment, error)

Deploy creates a deployment for the current app config and enqueues it. registryOverride, when non-nil, uses a different registry credential for this one deploy; otherwise the app's configured RegistryID is used. tagOverride, when non-empty, deploys a specific image tag (image source) for this deploy.

func (*Service) DetachHostMount

func (s *Service) DetachHostMount(app *models.Application, preset string) error

DetachHostMount removes a privileged host bind from the app.

func (*Service) DetachVolume

func (s *Service) DetachVolume(app *models.Application, volumeID uint) error

DetachVolume removes a volume mount from the app.

func (*Service) EnsureExecAllowed

func (s *Service) EnsureExecAllowed(workspaceID uint) error

EnsureExecAllowed returns a CapabilityDenied error when the workspace's plan does not permit opening an interactive shell into a container. Nil-safe on a disabled quota service (returns nil).

func (*Service) EnsurePublished

func (s *Service) EnsurePublished(ctx context.Context, appID uint) error

EnsurePublished reconciles the host ports an app's running container publishes with its approved bindings, enqueuing a rolling redeploy when they differ — either to open a newly-added port (Docker can't add one to a running container) or to drop one whose binding was released (e.g. its route was deleted). Idempotent and best-effort: a no-op when the live set already matches, the app isn't running, or the node is offline. Satisfies the route service's PortPublisher.

func (*Service) Exec

Exec opens an interactive command stream inside the app's active container. Returns ErrNoActiveContainer when nothing is running. The caller owns the returned stream and must Close it.

func (*Service) ExternalLabelTaken

func (s *Service) ExternalLabelTaken(label string, exceptAppID uint) (bool, error)

ExternalLabelTaken reports whether another application already owns the given external-access label. The label maps to a platform-wide host, so it must be unique across all workspaces; exceptAppID excludes the app being reconciled.

func (*Service) Get

func (s *Service) Get(workspaceID, id uint) (*models.Application, error)

func (*Service) GetDeployment

func (s *Service) GetDeployment(id uint) (*models.Deployment, error)

func (*Service) GetRelease

func (s *Service) GetRelease(app *models.Application, releaseID uint) (*models.Release, error)

GetRelease loads a release and verifies it belongs to the application.

func (*Service) IDByUID

func (s *Service) IDByUID(uid string) (uint, error)

IDByUID resolves an application's portable uid to its numeric id.

func (*Service) ImportEnvVars

func (s *Service) ImportEnvVars(appID uint, content string, isSecret bool) (int, error)

ImportEnvVars upserts every variable parsed from .env-style content (later duplicate keys win), encrypting them when isSecret. Returns the number set.

func (*Service) List

func (s *Service) List(workspaceID uint) ([]models.Application, error)

func (*Service) ListDeployments

func (s *Service) ListDeployments(appID uint, limit int) ([]models.Deployment, error)

func (*Service) ListEnvVars

func (s *Service) ListEnvVars(appID uint) ([]models.AppEnvVar, error)

func (*Service) ListReleases

func (s *Service) ListReleases(appID uint) ([]models.Release, error)

func (*Service) LiveStatus

func (s *Service) LiveStatus(ctx context.Context, app *models.Application) LiveStatus

LiveStatus inspects the app's active container and returns its real-time status (plus a stats snapshot when running). Falls back to the stored status when there is no running container.

func (*Service) MarkRedeployRequired

func (s *Service) MarkRedeployRequired(app *models.Application) (bool, error)

func (*Service) Overview

func (s *Service) Overview(app *models.Application) *Overview

Overview aggregates summary fields and resource counts for an application.

func (*Service) Processes

func (s *Service) Processes(ctx context.Context, app *models.Application, psArgs string) (docker.ProcessList, error)

Processes lists the running processes in the app's active container (the "docker top" view). Read-only — no shell capability needed. Returns ErrNoActiveContainer when nothing is running.

func (*Service) PromoteCanary

func (s *Service) PromoteCanary(app *models.Application) (*models.Deployment, error)

PromoteCanary makes the canary the new stable release. It enqueues a normal deploy of the canary's image; the deploy pipeline retires the old stable and the in-progress canary, leaving a single full-traffic release.

func (*Service) Redeploy

func (s *Service) Redeploy(app *models.Application) (*models.Deployment, error)

Redeploy enqueues a deploy that applies the app's current configuration. Used by Start/Restart when a redeploy is required.

func (*Service) Reencrypt

func (s *Service) Reencrypt(ctx context.Context, workspaceID uint) (int, error)

Reencrypt re-encrypts every secret env var of the workspace's applications under the workspace's active DEK. Non-secret vars (stored plaintext) are skipped. Idempotent; returns the number rewritten.

func (*Service) ResourceLimits

func (s *Service) ResourceLimits() (maxCPUCores, maxMemoryMB int)

ResourceLimits returns the platform per-app CPU/memory caps (0 = unlimited), for surfacing as hints in the UI.

func (*Service) Restart

func (s *Service) Restart(ctx context.Context, app *models.Application) (*models.Deployment, error)

Restart restarts the app's active container. If a redeploy is required (config changed while stopped), it redeploys instead so the new config is applied rather than restarting a stale container — returning that deployment so the caller can follow its logs. A plain restart returns a nil deployment.

func (*Service) RevealEnvVar

func (s *Service) RevealEnvVar(appID uint, key string) (string, error)

RevealEnvVar returns the decrypted value of a single env var, scoped to the app (the caller resolves the app within the workspace). Secret values are decrypted; non-secret values are returned as-is. Used by the audited reveal endpoint so an operator can read a value they otherwise only see masked.

func (*Service) Rollback

func (s *Service) Rollback(app *models.Application, releaseID uint) (*models.Deployment, error)

Rollback re-deploys a previous release's image.

func (*Service) Scale

func (s *Service) Scale(ctx context.Context, app *models.Application, replicas int) error

Scale sets the replica count of a cluster (service) app and applies it to the live Swarm service immediately (no redeploy). Rejected for container apps or when cluster mode is off.

func (*Service) SetCanaryWeight

func (s *Service) SetCanaryWeight(ctx context.Context, app *models.Application, weight int) error

SetCanaryWeight changes the share of traffic sent to the canary (1–99) and re-syncs the route immediately — no redeploy, no downtime.

func (*Service) SetClusterCap

func (s *Service) SetClusterCap(c ClusterCap)

SetClusterCap wires the cluster-capability check (nil-safe; nil means cluster mode is treated as off, so service-runtime apps are rejected).

func (*Service) SetContainerLabels

func (s *Service) SetContainerLabels(app *models.Application, labels map[string]string) error

SetContainerLabels validates and persists an app's user-defined Docker labels, enforcing the admin gate (§ plan capability + global kill-switch) and the reserved-prefix protection. Labels take effect on the next deploy (the caller marks the app redeploy-required, exactly like ports/volumes/env edits).

func (*Service) SetEnvVar

func (s *Service) SetEnvVar(appID uint, key, value string, isSecret bool) error

func (*Service) SetName

func (s *Service) SetName(app *models.Application, newName string) error

SetName validates and applies a new handle to app in memory (the caller persists via Update). The value is normalized to canonical slug form and must be non-empty and unique within the workspace. Unlike create it does not auto-suffix, so a rename onto a taken handle is an error. A no-op when unchanged. Mirrors workspace.SetName.

func (*Service) SetNetworkEnsurer

func (s *Service) SetNetworkEnsurer(e NetworkEnsurer)

SetNetworkEnsurer wires default-network self-healing (nil-safe). Without it, SetNetworks still attaches an already-existing default network, but cannot create a missing one.

func (*Service) SetNetworks

func (s *Service) SetNetworks(app *models.Application, networkIDs []uint) error

SetNetworks attaches the given workspace networks to the app, always including the workspace's default network.

The default is not optional garnish: it is the network the app shares with its databases, and in cluster mode it is the workspace's Swarm overlay — the thing that lets it reach a database on another node. An app that ends up on none deploys perfectly happily and then cannot resolve anything, with nothing to say why.

So a missing default is repaired rather than tolerated. That path is reachable for a workspace that predates default networks, or one whose network was removed out of band — and it matters most for callers that never name a network at all, like GitOps, where the default is the only network the app was ever going to get.

func (*Service) SetNodeGuard

func (s *Service) SetNodeGuard(g NodeGuard)

SetNodeGuard wires the placement guard consulted when creating an app on a node.

func (*Service) SetNodeNamer

func (s *Service) SetNodeNamer(n NodeNamer)

SetNodeNamer wires the swarm-node-id → name resolver used to annotate a cluster app with where its replicas actually run.

func (*Service) SetPortBindings

func (s *Service) SetPortBindings(r *repositories.PortBindingRepository)

SetPortBindings wires the port-binding repository used by EnsurePublished to learn which host ports an app's container must publish (injected after construction).

func (*Service) SetPorts

func (s *Service) SetPorts(app *models.Application, specs []PortSpec) error

SetPorts replaces an application's declared container ports and keeps the primary Port field in sync with the first declared port.

func (*Service) SetQuota

func (s *Service) SetQuota(q *quota.Service)

SetQuota wires the plan/quota enforcer (nil-safe; nil skips checks).

func (*Service) SetReleasePinned

func (s *Service) SetReleasePinned(app *models.Application, releaseID uint, pinned bool) (*models.Release, error)

SetReleasePinned protects (or unprotects) a release from cleanup deletion.

func (*Service) SetRouteSyncer

func (s *Service) SetRouteSyncer(rs RouteSyncer)

SetRouteSyncer wires the route reconciler used by canary traffic changes.

func (*Service) SetServerInfo

func (s *Service) SetServerInfo(si ServerInfo)

SetServerInfo wires the resolver used to annotate apps with their node's name.

func (*Service) SetSettings

func (s *Service) SetSettings(p *settings.Provider)

SetSettings wires the platform settings provider used to enforce resource caps.

func (*Service) SetWorkspaceInfo

func (s *Service) SetWorkspaceInfo(w WorkspaceInfo)

SetWorkspaceInfo wires the resolver used to check a workspace's privileged flag.

func (*Service) Start

func (s *Service) Start(ctx context.Context, app *models.Application) (*models.Deployment, error)

Start starts the app's (stopped) active container. If a redeploy is required (config changed while stopped), it redeploys instead so the new config is applied rather than starting a stale container — returning that deployment so the caller can follow its logs. A plain start returns a nil deployment.

func (*Service) StartCanary

func (s *Service) StartCanary(app *models.Application, registryOverride *uint, tagOverride string) (*models.Deployment, error)

func (*Service) Stop

func (s *Service) Stop(ctx context.Context, app *models.Application) error

Stop stops the app's active container (it stays stopped; no auto-restart). For a service app it scales the service to zero replicas (the desired count stays recorded on the app, so Start restores it).

func (*Service) Update

func (s *Service) Update(app *models.Application) error

func (*Service) WaitRunning

func (s *Service) WaitRunning(ctx context.Context, app *models.Application) error

WaitRunning polls the app's active container until it reports the running state, or returns an error on timeout. Used as a basic readiness gate for rolling restarts so the next app isn't restarted until this one recovers.

type WorkspaceInfo

type WorkspaceInfo interface {
	FindByID(id uint) (*models.Workspace, error)
}

WorkspaceInfo resolves a workspace's trust flags (privileged). Implemented by the workspace repository; injected after construction. Used to gate privileged host mounts.

Jump to

Keyboard shortcuts

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