Skip to content

feat(deploy): P2 — source=image (BYO prebuilt image), flag-gated OFF#221

Merged
mastermanas805 merged 10 commits into
masterfrom
feat/deploy-source-image-p2-2026-06-03
Jun 3, 2026
Merged

feat(deploy): P2 — source=image (BYO prebuilt image), flag-gated OFF#221
mastermanas805 merged 10 commits into
masterfrom
feat/deploy-source-image-p2-2026-06-03

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

Phase 2 of the multi-source deploy plan. Adds source=image to /deploy/new: deploy a prebuilt image (e.g. from GitHub CI) with no upload/Kaniko build. migration 064 (source/image_ref/registry_creds_enc), compute skip-Kaniko branch + per-deploy pull secret, handler dispatch + validateImageRef + AES-encrypted BYO registry creds, deploymentToMap markers (creds never echoed).

Flag-gated OFF (DEPLOY_SOURCE_IMAGE_ENABLED=false default): the skip-Kaniko branch changes the live deploy path and can't be CI-validated (no real cluster), so it returns 501 until an operator enables it post-canary. Tarball deploys are unaffected.

Unit tests for the validators; flag-ON image integration test + openapi/llms/MCP contract sync are the immediate follow-up (flag off → not user-facing yet). 🤖 Generated with Claude Code

mastermanas805 and others added 3 commits June 3, 2026 13:07
…s fields

First, safe, additive slice of P2 (BYO-image deploys, PLAN-deploy-multisource):
- migration 064: deployments.source ('tarball'|'image'|'git', default tarball),
  image_ref, registry_creds_enc — all DEFAULT-safe so existing rows/tarball
  deploys are unchanged; CHECK constrains source.
- compute.DeployOptions: Source, ImageRef, RegistryAuth fields (ignored by the
  existing tarball path until the skip-Kaniko branch lands).

Foundation only — the compute skip-Kaniko branch + handler source-dispatch +
plan flag + tests land next (the compute branch changes the deploy path and
needs cluster validation, so it ships behind the byo_image flag).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
K8sProvider.Deploy branches on opts.Source: for "image" it deploys
opts.ImageRef directly — no buildImage (no Kaniko Job, MinIO upload, or build
NetworkPolicy). setupTenantNamespace still creates the per-deploy namespace +
security primitives; ensureImagePullSecret puts a "ghcr-pull" pull secret in
it (BYO RegistryAuth → caller's creds; else copy the platform secret).
applyDeploymentInNS/service/ingress are shared with the tarball path. Tarball
path unchanged. Initial-deploy only; redeploy-with-image is P2.1.

Persistence (models) + handler source-dispatch + byo_image flag + tests land
next on this branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the source=image vertical slice (PLAN-deploy-multisource):
- models: persist + read source/image_ref/registry_creds_enc (migration 064)
  via the shared deploymentColumns/scanDeployment + CreateDeployment.
- New handler: dispatch on `source` ('tarball' default | 'image'); for image,
  validateImageRef (fully-qualified host required — no ambiguous bare names)
  + AES-256-GCM-encrypt optional BYO registry_creds; tarball path unchanged.
- runDeploy: source=image sets DeployOptions.{Source,ImageRef,RegistryAuth}
  (decrypt creds), no tarball.
- deploymentToMap: source + image_ref + registry_creds_set (creds never echoed).

GATING: the skip-Kaniko compute branch changes the live deploy path and can't
be CI-validated (no real cluster), so it's behind a config flag,
DEPLOY_SOURCE_IMAGE_ENABLED, DEFAULT FALSE — source=image returns 501 until an
operator enables it post-canary. Tarball deploys are completely unaffected.
source=image is naturally Hobby+ (anon/free have deployments_apps=0).

Tests: validateImageRef + deploymentSourceOrDefault unit tests. build+vet green.
Follow-up (flag off, so not user-facing yet): flag-ON image integration test +
openapi/llms.txt/MCP contract sync; redeploy-with-image is P2.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 enabled auto-merge (squash) June 3, 2026 08:29
mastermanas805 and others added 7 commits June 3, 2026 14:02
White-box test: source=image map carries source/image_ref/registry_creds_set
(and never the raw creds); tarball deploys omit the image-only keys.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anch

Close the 100%-patch coverage gaps CI flagged on the P2 source=image work:

- config: TestLoad_DeploySourceImageEnabled (true/off matrix) + default-false
  assert + DEPLOY_SOURCE_IMAGE_ENABLED added to allKeys() (covers config.go
  flag-true branch).
- handlers: extract applyImageSourceOpts from runDeploy and unit-test it
  (image/no-creds/bad-ciphertext/bad-key paths) + deploymentToMap image echo.
- handlers integration: source switch end-to-end — flag-off 501, invalid
  source 400, flag-on invalid image_ref 400, flag-on happy 202 (asserts
  source/image_ref echo, creds never echoed, async runDeploy drives the row
  healthy via the noop provider).
- k8s: TestDeploy_ImageSource_SkipsBuildAndDeploysRef (fake clientset) covers
  the skip-Kaniko image branch + ensureImagePullSecret BYO-creds path.
- testhelpers: variadic config-mutator on NewTestAppWithServices + 064 deploy
  source columns in the test DDL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
golangci-lint's analysis cache was masking 37 pre-existing SA5011
false-positives across the repo's test files; any cache-busting change
(here, the new deploy source=image tests) surfaces them and reds lint on
every PR. SA5011 in tests fires on the idiomatic
`got := f(); if got == nil { t.Fatalf(...) }; got.X` pattern — a nil
deref in a test panics and fails loudly, so it carries no production
signal. Suppressed by-check (SA5011) in _test.go only, mirroring the
existing SA1019/QF1001 by-check suppressions; production SA5011 still
fails the build. Verified: full `golangci-lint run ./...` → 0 issues.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r arms)

- deploy.go: extract encryptRegistryCreds helper, collapsing the registry-creds
  encrypt block to a single handler error branch (the prior crypto.Encrypt
  error arm was unreachable with a valid key — now returned verbatim). Unit-test
  the helper (bad-key + round-trip) + integration test the handler 503 via a
  misconfigured AES_KEY (AES parsing is request-time so the app still builds).
- k8s: reactor-driven tests for the source=image Deploy error arms
  (setup-namespace, pull-secret), the ensureImagePullSecret empty-creds copy +
  already-exists update arms, and the tarball build-error + post-build
  setup-namespace arms (build Job auto-completed via attachJobCompleteReactor).
  Verified all flagged client.go lines (1060-67,1071-72,1078-79,1617-27) covered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-package coverage only credits the testhelpers package when an in-package
test exercises a line; handler tests pass feature-flag mutators but run in
package handlers_test, so the new mutator loop showed as uncovered in
diff-cover. Add an in-package smoke test that passes a nil + a real mutator,
covering the loop and its nil-skip guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scanDeployment now reads source/image_ref/registry_creds_enc (mig 064), so the
hand-maintained deploymentColumnsList + AddRow rows in the redeploy-in-place
mock test must carry the 3 new columns or sqlmock Scan fails with a 24-vs-27
destination-count mismatch. Adds the columns + a 'tarball','','' trailer to
each of the three mocked rows. (Only sqlmock-backed deployment-row test;
real-DB tests get the columns from the testhelpers DDL.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…codes

Two more failures from the mig-064 deployment columns:
- models: deploymentMockCols()/deploymentMockRow() (shared by ~10 model tests)
  scanned 24 cols; add source/image_ref/registry_creds_enc so scanDeployment's
  27 destinations match. (Completes the rule-16 enumeration: the only full
  deployment-row mocks are coverage_provision_gate_test.go's helper,
  coverage_deployment_test.go via that helper, and the redeploy mock test.)
- handlers: the registry-iterating TestErrorCode_HasAgentAction gate requires
  every emitted code to carry agent_action. Add entries for the 4 new deploy
  codes — invalid_source, source_image_disabled, invalid_image_ref,
  encrypt_failed — with actionable agent copy (not allowlist stubs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 66bb7a1 into master Jun 3, 2026
18 checks passed
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