feat(build): make the user-modpack build lane read its context (closes the last functional gap)
A submitted modpack was durable but unreadable: the uploads PVC cannot cross
namespaces (felis-api mounts it; Kaniko runs in felis-build) and the s3 lane
handed the sandboxed build Pod no credentials, so NO user build could ever
consume its context. The transport is now the API itself:
- submit: derived context refs become the internal-face URL
/api/v1/internal/submissions/{id}/context (service-token gated), and Blobs
gains Open (local + s3) with an ErrBlobNotFound sentinel for the route's 404.
- api: serves that route on the internal face only (openapi.yaml updated; the
route-coverage test enforces it).
- build: an http(s) context renders a context-fetch initContainer (the felis
image's new fetch-context entrypoint) that streams the blob with the
namespace-local service-token Secret — never mounted into Kaniko — and
extracts it under a zip-slip guard into a size-limited emptyDir that Kaniko
reads read-only as --context=/context.
- platform/install: the api Deployment carries its own internal base URL; the
build namespace gets the token Secret through the existing replica mechanism
(bootstrap.sh + felis setup); the build egress lock opens exactly the control
namespace on the internal port.
- cmd/felis: fetch-context entrypoint (registered, documented, unit-tested for
escapes/symlinks/non-gzip).
Tests cover rendering, hardening, the s3/local Open paths, and the route's
404/503 mapping. Verified next on the real single-node cluster with Kaniko.
This commit is contained in:
26 files changed
+1080
-72
No files matched your search
@@ -78,9 +78,12 @@ func Objects(p Params) []Object {
|
||||
|
||||
// Build-namespace egress lock (reused from internal/build; TypeMeta stamped).
|
||||
buildNP := build.BuildNetworkPolicy(build.NetPolParams{
|
||||
Namespace: p.BuildNamespace,
|
||||
RegistryNamespace: p.RegistryNamespace,
|
||||
RegistryPort: p.RegistryPort,
|
||||
Namespace: p.BuildNamespace,
|
||||
RegistryNamespace: p.RegistryNamespace,
|
||||
RegistryPort: p.RegistryPort,
|
||||
ControlNamespace: p.ControlNamespace,
|
||||
// The internal face's port, single-sourced with the api Deployment below.
|
||||
APIPort: apiInternalPort,
|
||||
PackageSourceCIDRs: p.PackageSourceCIDRs,
|
||||
})
|
||||
buildNP.TypeMeta = metav1.TypeMeta{APIVersion: "networking.k8s.io/v1", Kind: "NetworkPolicy"}
|
||||
|
||||
@@ -274,6 +274,10 @@ func APIDeployment(p Params) *appsv1.Deployment {
|
||||
},
|
||||
},
|
||||
{Name: "FELIS_IMAGE", Value: p.FelisImage},
|
||||
// The api's own internal-face base URL, so it derives the submission
|
||||
// context URLs that build Pods fetch through it. Same value the login gate
|
||||
// is handed; one address for one face.
|
||||
{Name: naming.EnvAPIBaseURL, Value: InternalAPIBaseURL(p.ControlNamespace)},
|
||||
}
|
||||
if p.BackupPVC != "" {
|
||||
env = append(env, corev1.EnvVar{Name: "FELIS_BACKUP_PVC", Value: p.BackupPVC})
|
||||
|
||||
@@ -179,6 +179,11 @@ func TestAPIDeployment_Wiring(t *testing.T) {
|
||||
if v := envValue(c.Env, "FELIS_IMAGE"); v != p.FelisImage {
|
||||
t.Errorf("FELIS_IMAGE = %q, want %q", v, p.FelisImage)
|
||||
}
|
||||
// The internal-face base URL the api derives submission context-fetch URLs
|
||||
// from — the same address the login gate is handed.
|
||||
if v := envValue(c.Env, "FELIS_API_BASE_URL"); v != InternalAPIBaseURL(p.ControlNamespace) {
|
||||
t.Errorf("FELIS_API_BASE_URL = %q, want %q", v, InternalAPIBaseURL(p.ControlNamespace))
|
||||
}
|
||||
// FELIS_SERVICE_TOKEN must come from a Secret, never a literal value.
|
||||
tok := envVar(c.Env, "FELIS_SERVICE_TOKEN")
|
||||
if tok == nil || tok.ValueFrom == nil || tok.ValueFrom.SecretKeyRef == nil {
|
||||
|
||||
Reference in new issue
Block a user