Skip to content

feat(k8s): bridge any workload kind as a Deployment - #15

Merged
vercel-eddie merged 4 commits into
mainfrom
eddie/cronjob-source
Sep 30, 2026
Merged

vercel-eddie merged 4 commits into
mainfrom
eddie/cronjob-source

Conversation

@vercel-eddie

@vercel-eddie vercel-eddie commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Lets a bridge be created from any workload kind, not just a Deployment. Today bridge create --source with, say, a CronJob's manifests fails with no deployment found in bundle, so a cron service can't be developed or tested against staging the way a Deployment can.

How

One conversion for every kind, DeploymentFromWorkload(kind, obj): take the workload's pod template and its metadata, and build a single-replica Deployment from the two. Everything after sourcing is unchanged: the transforms, save, the proxy and the devcontainer only ever see that Deployment.

  • WorkloadKind is an interface with GVK() and PodTemplatePath(), implemented once per supported kind:

    kind pod template
    Deployment, StatefulSet, DaemonSet, ReplicaSet, Job spec.template
    CronJob spec.jobTemplate.spec.template

    The template is read at the kind's own path. A supported kind with no template there is an error, and other kinds aren't bridged.

  • Metadata: name, namespace, labels, annotations.

  • restartPolicy becomes Always and activeDeadlineSeconds is dropped, since a Deployment's pods allow neither. A template with no labels gets app: <name> for its selector, which TransformSelectors then replaces anyway.

  • A bridge made from a Job or CronJob runs its pod, not its schedule or completion. You run the job in the devcontainer with bridge exec.

Where

  • --source manifests: every document of a supported kind becomes a Deployment; everything else is handled as before. They're ordered by kind (Deployment, StatefulSet, DaemonSet, ReplicaSet, Job, CronJob), so a Deployment is the source whenever the manifests have one.
    • A converted workload isn't also saved as itself. Previously a CronJob next to a Deployment was saved as a bdg-* CronJob, which ran on its schedule in the cluster.
    • A source Deployment is also rebuilt from its template and metadata now. InjectProxyImage already forced one replica, and nothing reads the other spec fields (strategy, etc.).
  • bridge create <name>: looks the name up through the dynamic client across those kinds, in that order, so a Deployment still wins a name clash. None found → WorkloadNotFoundError, mapped to gRPC NotFound like DeploymentNotFoundError.

This is administrator-side (pkg/k8s/resources), so it takes effect once the administrator runs a build with it.

Testing

  • workload_test.go:
    • every WorkloadKind converts, keeping name, namespace, labels, annotations, service account and env, with one replica, Always restart and no deadline
    • a workload with no template at its kind's path is an error, and an unlabeled template gets a selector
    • a CronJob-only bundle runs through the full manifest pipeline and saves a Deployment, not a CronJob
    • a Deployment next to a CronJob is the source, and a ConfigMap stays a ConfigMap
    • namespace lookup via dynamic/fake: finds a CronJob, prefers a same-named Deployment, and not-found
  • The whole suite passes (go test ./... without e2e, 12 packages), go vet is clean, and bridge create --help is unchanged from main.
  • Live against staging (venus), with this build run as a local bridge administrator and --admin-addr 127.0.0.1:19090:
    • bridge create cron-managed-drives-pruner with no --source found venus's real CronJob by name. Its bridge pod came up with the CronJob's service account (oidc-cron-managed-drives-pruner).
    • The same CronJob from --source manifests: bridge exec ran the job to completion as that role.
    • Regression check: api-managed-drives from Deployment manifests came up with its own service account.
    • No bridge CronJob was created in any of these.

🤖 Generated with Claude Code

vercel-eddie and others added 2 commits September 30, 2026 12:48
A CronJob can now be a bridge's source. It's bridged as a single-replica
Deployment running the CronJob's pod template, so the bridge gets the job's
service account, env and volumes, but not its schedule; run the job in the
devcontainer with `bridge exec`. restartPolicy becomes Always and
activeDeadlineSeconds is dropped, since a Deployment's pods allow neither.

- --source manifests with no Deployment bridge their CronJob (the first by
  name, if there are several). It isn't also saved as a CronJob. Manifests
  that have a Deployment are unchanged.
- `bridge create <name>` falls back to a CronJob of that name when there's
  no Deployment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Replaces the CronJob-specific conversion with one that works for every
workload kind: take the workload's pod template (spec.template, or
spec.jobTemplate.spec.template for a CronJob) and its metadata, and build a
single-replica Deployment from them. The rest of the pipeline only ever sees
that Deployment.

- --source manifests: every document with a pod template becomes a
  Deployment, ordered by kind (Deployment, StatefulSet, DaemonSet,
  ReplicaSet, Job, CronJob), so a Deployment is the source whenever the
  manifests have one. A converted workload isn't also saved as itself.
- `bridge create <name>` looks the name up with the dynamic client across
  those kinds, in that order, and returns WorkloadNotFoundError (NotFound
  over gRPC) when none has it.
- Drops the CronJob example from `bridge create --help`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel-eddie vercel-eddie changed the title feat(k8s): bridge a CronJob as a Deployment feat(k8s): bridge any workload kind as a Deployment Sep 30, 2026
vercel-eddie and others added 2 commits September 30, 2026 13:11
WorkloadKind is an interface with GVK() and PodTemplatePath(), implemented
once per supported kind (Deployment, StatefulSet, DaemonSet, ReplicaSet, Job,
CronJob). DeploymentFromWorkload takes the kind and reads the template at its
path, instead of trying every path on any object; a supported kind with no
template there is an error. Only those kinds are bridged as a Deployment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@vercel-eddie
vercel-eddie merged commit a570e94 into main Sep 30, 2026
4 checks passed
@vercel-eddie
vercel-eddie deleted the eddie/cronjob-source branch September 30, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant