Documentation
¶
Overview ¶
Package kubernetes projects a parsed devcontainer.json (plus its resolved features) into the Kubernetes API objects that would launch the same container on a cluster.
The package is pure: no client-go, no apiserver round-trips. Project returns a *corev1.Pod (and any ancillary objects) so the caller — an operator's controller, a CLI's render command, an admission webhook — can decide how to apply, validate, or serialize them.
The translation mirrors the field mapping that pkg/host/docker performs against the docker engine API. Where docker has a single flat HostConfig, Kubernetes splits the same surface across PodSpec, SecurityContext, Resources, and Volumes — Project handles the re-shaping and rejects fields that don't have a clean Kubernetes equivalent rather than silently dropping them.
Embedding ¶
pj, err := kubernetes.Project(kubernetes.ProjectRequest{
Spec: dc,
SpecPath: dc.Path,
LocalWorkspace: workspace,
Image: "registry.example.com/devbox:1.2.3",
Name: "alice-devbox",
Namespace: "dev",
})
// apply pj.Pod with controller-runtime, kubectl apply -f, etc.
Lifecycle commands ¶
Project does not embed onCreate/postCreate/postStart/postAttach into initContainers or lifecycle hooks: those phases need the running container's userspace state (features installed by previous phases, the workspace bind contents) and can't be modeled as static manifest fields. The imperative pkg/host/kubernetes execs them after the pod reaches Ready, the same shape pkg/host/docker uses today.
Index ¶
Constants ¶
const ( AnnotationComposeProject = "devcontainer.compose.project" AnnotationComposeService = "devcontainer.compose.service" )
AnnotationComposeProject and AnnotationComposeService record which compose project/primary-service produced the Pod. Useful for an operator that wants to correlate a Pod back to the source spec without re-parsing.
const ( AnnotationLocalFolder = "devcontainer.local_folder" AnnotationConfigFile = "devcontainer.config_file" )
Annotations written onto the projected Pod so upstream tooling (and pkg/host/kubernetes) can reverse-look-up which devcontainer / workspace produced the pod. Keys mirror the docker host labels exactly — see pkg/host/docker.LabelLocalFolder / LabelConfigFile — but live on annotations rather than labels because path values aren't valid Kubernetes label values.
const DefaultContainerName = "devcontainer"
DefaultContainerName is the container name used when the request doesn't supply one.
Variables ¶
var DefaultComposeVolumeStorage = resource.MustParse("1Gi")
DefaultComposeVolumeStorage is the storage request applied to PVCs generated for compose named volumes when the request doesn't set one. Small enough that a 1Gi-default storage class is happy; large enough that a basic devcontainer workflow has somewhere to write.
Functions ¶
Types ¶
type ComposeProjectRequest ¶
type ComposeProjectRequest struct {
// Spec is the parsed devcontainer.json (must be in compose mode).
Spec *spec.DevContainer
// SpecPath is the host path the spec was loaded from. Stored as
// AnnotationConfigFile.
SpecPath string
// LocalWorkspace is the host workspace path. Stored as
// AnnotationLocalFolder and used as the default workspace
// HostPath source.
LocalWorkspace string
// ContainerWorkspace is where the workspace mounts inside the
// primary container. Falls back to spec.WorkspaceFolder, then
// "/workspaces/<basename>".
ContainerWorkspace string
// Project is the parsed compose project. Required.
Project *composetypes.Project
// PrimaryImage, when non-empty, replaces the primary service's
// compose `image:`. Used to point the Pod at a feature-derived
// image produced by pkg/host/dockercompose.BuildPrimaryImage.
PrimaryImage string
// Features as resolved by pkg/features. Their containerEnv,
// init/privileged/capAdd/securityOpt, and mounts merge into the
// primary container only — sidecar services don't see them.
Features []features.Resolved
// Name and Namespace seed metadata.
Name string
Namespace string
// Labels and Annotations seed metadata.
Labels map[string]string
Annotations map[string]string
// WorkspaceVolumeSource overrides the workspace volume source.
// When nil, falls back to a HostPath sourced from LocalWorkspace
// (matching the single-container projection's default).
WorkspaceVolumeSource *corev1.VolumeSource
// VolumeStorageClass is set on PVCs generated for compose named
// volumes. Empty means the cluster's default storage class.
VolumeStorageClass string
// VolumeStorageRequest is the per-PVC storage request. Defaults
// to DefaultComposeVolumeStorage when zero.
VolumeStorageRequest resource.Quantity
// Profiles activates compose service profiles. Services whose
// `profiles:` list is non-empty are excluded from the projected
// Pod unless one of their entries matches Profiles (or Profiles
// contains "*"). The primary service stays enabled regardless.
Profiles []string
// LocalEnv is the lookup table for compose `secrets:` /
// `configs:` declared with `environment:` source. Materializing
// happens at projection time, so the env variable's value is
// baked into the resulting Secret/ConfigMap.
LocalEnv map[string]string
}
ComposeProjectRequest captures the inputs needed to render a compose-mode devcontainer into a single multi-container Pod plus any backing PVCs.
The shape mirrors ProjectRequest where it can — Spec, Image override, Features, metadata, workspace — and adds compose-specific inputs: the parsed *types.Project (use pkg/host/dockercompose.LoadProject to produce it) and the storage knobs for named-volume PVCs.
type ComposeProjection ¶
type ComposeProjection struct {
Pod *corev1.Pod
PersistentVolumeClaims []*corev1.PersistentVolumeClaim
ConfigMaps []*corev1.ConfigMap
Secrets []*corev1.Secret
}
ComposeProjection bundles the rendered Pod with any PVCs, Secrets, and ConfigMaps that need to exist for the Pod's volume references to resolve. The caller applies them in order (PVCs/ConfigMaps/ Secrets first, then the Pod).
func ProjectCompose ¶
func ProjectCompose(req ComposeProjectRequest) (*ComposeProjection, error)
ProjectCompose translates a compose-mode devcontainer into a single multi-container Pod (sidecar model) plus PVCs for any compose named volumes.
Scope cuts in this iteration (mirroring pkg/host/dockercompose so behavior stays consistent across hosts):
- depends_on: containers in a Pod can't be ordered through the compose model; compose `depends_on` is ignored.
- watch / develop: file-watch loops aren't a static manifest concept and aren't honored by the projection. The imperative pkg/host/dockercompose host has Watch() for that.
- Compose-side `build:` for any service (including the primary): not built. Each service must reference a pullable `image:`.
- tmpfs/image/cluster volume types: tmpfs translates to EmptyDir(memory); image and cluster types return an error.
Profiles, configs, secrets, and service-name DNS are honored:
- Profiles narrow the projected Pod to services whose `profiles:` match `req.Profiles` (with "*" as a wildcard). The primary stays enabled regardless.
- Configs become ConfigMaps (or external references) and are mounted at `/<source>` by default.
- Secrets become Secrets (or external references) and are mounted at `/run/secrets/<source>` by default.
- Service-name DNS works inside the Pod via pod.spec.hostAliases: each compose service name (and any compose network alias) is mapped to 127.0.0.1, where its container is actually listening since all containers in a Pod share the network namespace. So `db:5432` resolves the same way it would on a compose user-defined network.
func (*ComposeProjection) Objects ¶
func (p *ComposeProjection) Objects() []runtime.Object
Objects returns the Kubernetes resources needed for a compose projection, in creation order.
type ProjectRequest ¶
type ProjectRequest struct {
// Spec is the parsed devcontainer.json. Required.
Spec *spec.DevContainer
// SpecPath is the host-side path the spec was loaded from. When
// non-empty it's stored in the AnnotationConfigFile annotation so
// downstream tooling can reverse-look-up the source file.
SpecPath string
// LocalWorkspace is the host-side workspace folder (absolute). When
// non-empty it's stored in AnnotationLocalFolder and used as the
// default HostPath source for the workspace volume.
LocalWorkspace string
// ContainerWorkspace is the path inside the container where the
// workspace is mounted. Falls back to spec.WorkspaceFolder, then
// "/workspaces/<basename>".
ContainerWorkspace string
// Image is the already-built container image reference.
Image string
// Features as resolved by pkg/features. Their containerEnv,
// init/privileged/capAdd/securityOpt, and mounts merge into the
// pod the same way the docker host merges them.
Features []features.Resolved
// Name is the metadata.name written onto the projected Pod. May be
// empty — the caller can fill it in or use generateName.
Name string
// Namespace is metadata.namespace. May be empty.
Namespace string
// ContainerName overrides the container's name. Defaults to
// DefaultContainerName.
ContainerName string
// Labels and Annotations are seeded onto metadata. The package adds
// AnnotationLocalFolder and AnnotationConfigFile on top.
Labels map[string]string
Annotations map[string]string
// WorkspaceVolumeSource overrides the default workspace volume.
// When nil, a HostPath sourced from LocalWorkspace is used — a fit
// for single-node clusters (kind, minikube, docker-desktop) where
// the host workspace is reachable from the kubelet. Set to a
// PersistentVolumeClaim or other source for remote clusters.
WorkspaceVolumeSource *corev1.VolumeSource
}
ProjectRequest captures the inputs needed to render a devcontainer into Kubernetes objects. The caller is responsible for building the image (use pkg/builder) and resolving features (use pkg/features); on Kubernetes there's no equivalent of the docker host's host-side variable substitution loop, so any ${localEnv:VAR} / ${devcontainerId} references in the spec should already be resolved before Project is called (the substitute package can do this).
type Projection ¶
Projection is the rendered Kubernetes object set. Today only Pod is populated; future fields (Service for forwardPorts, PVC for the workspace) will be additive.
func Project ¶
func Project(req ProjectRequest) (*Projection, error)
Project translates a devcontainer spec into Kubernetes manifests.
It rejects with an error any spec field or runArg that has no clean Kubernetes equivalent (e.g. --network, --gpus device IDs, named docker volumes), rather than silently dropping it: a missing --cap-add or --userns flag changes the container's security model. Embedders that want the request to succeed regardless can strip the offending fields before calling.
func (*Projection) Objects ¶
func (p *Projection) Objects() []runtime.Object
Objects returns the Kubernetes resources needed for a single-container projection, in creation order.