<#958 Multi-architecture enrolling packages> Pull ...
# osctrl
g
#958 Multi-architecture enrolling packages Pull request opened by javuto Summary Implements multi-architecture enrolling packages so an environment can have multiple
.deb
,
.rpm
,
.pkg
, or
.msi
packages — each targeting a different architecture (amd64, arm64, x86_64, aarch64, universal). Closes #466. Problem The
TLSEnvironment
model stored one package URL per type (
DebPackage
,
RpmPackage
,
MsiPackage
,
PkgPackage
). An operator with both
amd64
and
arm64
Linux nodes needed two
.deb
and two
.rpm
packages, but the model only had room for one of each — no way to distinguish architecture. Changes New model (
pkg/environments/packages.go
) •
EnvironmentPackage
table — one row per (environment, type, architecture, URL) with
is_default
flag • CRUD methods:
GetPackages
,
GetPackagesByType
,
GetPackage
(with fallback chain: exact architecture → default → first available),
AddPackage
,
RemovePackage
,
SetDefaultPackage
•
MigrateLegacyPackages
— idempotent migration that copies existing single-package fields into the new table on first boot • Architecture validation (
ValidArchitectures
):
amd64
,
arm64
,
x86_64
,
aarch64
,
universal
Auto-migrate (
pkg/environments/environments.go
) •
CreateEnvironment
now auto-migrates
EnvironmentPackage
and runs the legacy migration TLS download handler (
cmd/tls/handlers/post.go
) • Checks the
EnvironmentPackage
table first (by type + arch), falls back to legacy single-package fields • New route:
GET /{env}/{secretpath}/package/{package}/{arch}
— serves architecture-specific packages • Old route:
GET /{env}/{secretpath}/package/{package}
— still works (backward compat) API handlers (
cmd/api/handlers/environments_packages.go
) | Method | Path | Description | | ------ | ---------------------------------------- | ------------------------------------------- | | GET | /api/v1/environments/{env}/packages | List all packages | | POST | /api/v1/environments/{env}/packages | Add a package (type, arch, url, is_default) | | DELETE | /api/v1/environments/{env}/packages/{id} | Remove a package | All require
AdminLevel
permissions and are audit-logged. Frontend (
frontend/src/features/enrollment/EnrollPage.tsx
) • New
PackageListCard
component below the existing
PackageUrlCard
• Shows all multi-architecture packages grouped by type (DEB / RPM / PKG / MSI) • Add form with type selector, architecture selector, and URL input • Remove button per package with
is_default
badge • API client functions:
listEnvPackages
,
addEnvPackage
,
removeEnvPackage
in
frontend/src/api/environments.ts
Tests (10 new in
pkg/environments/packages_test.go
) •
TestAddPackage_Success
— add two packages (amd64 + arm64) for the same type •
TestAddPackage_InvalidArchitecture
— rejects unknown architecture •
TestGetPackage_ByArchitecture
— exact architecture match •
TestGetPackage_FallbackToDefault
— falls back to
is_default
package when arch not found •
TestGetPackage_FallbackToFirstAvailable
— falls back to first by architecture order when no default •
TestRemovePackage_Success
— removes a package by ID •
TestRemovePackage_NotFound
— returns
ErrRecordNotFound
for missing ID •
TestSetDefaultPackage
— sets a new default and unsets the old one •
TestMigrateLegacyPackages
— migrates `DebPackage`/`RpmPackage` fields into the new table •
TestMigrateLegacyPackages_Idempotent
— running migration twice doesn't duplicate rows Backward compatibility • The legacy `DebPackage`/`RpmPackage`/`MsiPackage`/`PkgPackage` fields and their
Update*Package
methods are unchanged • The old TLS route (without
{arch}
) still works — serves from the new table or falls back to legacy fields • The legacy
PackageUrlCard
in the frontend is preserved alongside the new
PackageListCard
• Migration is idempotent — existing deployments are transparently upgraded on next boot Validation •
go build ./...
— clean •
go test ./...
— all packages pass (10 new tests) •
golangci-lint run ./pkg/environments/...
— 0 issues •
npm run check
— TypeScript typecheck clean •
npm test
— 34 test files / 209 tests pass •
gofmt
clean on all modified files jmpsec/osctrl