Skip to content

Multiple packages: packages[] API and add/edit/delete-package endpoints #48397

Description

@cdcme

Related user story

#28108

Task

Expose the multi-package model through the REST API. Add a packages[] array to the software-title list and detail responses, keep software_package as the first-added package for backwards compatibility, let upload add a package to an existing title, and let edit and delete target a specific package.

Depends on the data-model foundation (datastore list-all read and first-added read). Can be built in parallel with the install-precedence and GitOps sub-issues.

Response structs

  • Add Packages []SoftwareInstaller to fleet.SoftwareTitle (server/fleet/software.go:410, alongside SoftwarePackage at :443).
  • Add a list-shaped Packages []SoftwarePackageOrApp to fleet.SoftwareTitleListResult (:505, alongside :529).
  • Keep software_package populated with the first-added package (null when none). Why keep it: API clients and GitOps depend on it; [API] Add multiple custom packages for the same software title in the same fleet #48265 documents it as retained.

Service population

Endpoints

  • POST /api/_version_/fleet/software/package (server/service/handler.go:429): allow adding to an existing title (drop the "already has an installer" path for custom packages; preserve FMA/VPP guards), subject to Multiple packages: data model, migration, hash-dedupe guard, and multi-active regressions #48396's hash guard and 10-limit.
  • PATCH /api/_version_/fleet/software/titles/{id}/package (handler.go:432): operate on a specific package. Confirm the request identifies the package (installer_id) when a title has several. When a file replacement's hash matches a different package on the same title (UpdateSoftwareInstaller in ee/server/service/software_installers.go, after the new storage_id is computed near :501), return a friendly 409 (Couldn't edit. <file> package is already added (same SHA-256 hash).) instead of the raw 1062 the dedup_token key throws. Reuse Multiple packages: data model, migration, hash-dedupe guard, and multi-active regressions #48396's hash check with an exclude-self parameter (existingInstaller.ID); applies to script-only too (the file is the script).
  • Per-package delete: extend DELETE /api/_version_/fleet/software/titles/{title_id}/available_for_install (handler.go:433) to accept a specific installer_id, deleting one package while leaving the title and remaining packages intact. Deleting the last package (or an explicit title delete) keeps today's behavior. Follow the existing request/response struct patterns in server/service/software_installers.go.

Match the REST shapes documented in #48265 (trimmed for list, full for detail; packages.status counts combine automations, setup experience, and manual installs).

Condition of satisfaction

  • List + detail: GET /software/titles and GET /software/titles/:id return packages[] matching [API] Add multiple custom packages for the same software title in the same fleet #48265; software_package equals the first-added package and is null when none.
  • Add: uploading a second package to a title succeeds and appears in packages[]; FMA/VPP titles still reject custom packages with the documented copy.
  • Edit: PATCH updates the targeted package only. Replacing a file with a sibling package's hash returns the friendly 409 and leaves both packages unchanged; replacing with a new hash succeeds; re-saving the same file is a no-op. Covered for .pkg/.msi/.deb and script-only.
  • Delete: deleting one of several packages leaves the title and the rest intact; deleting the last package matches prior single-package delete behavior.
  • Authorization: observer, observer+, and gitops roles receive 403 on add/edit/delete at the API (backend-enforced). Free tier returns 402.
  • MYSQL_TEST=1 REDIS_TEST=1 go test ./server/service/... passes, including a test that adds two packages and asserts both response shapes.

Metadata

Metadata

Assignees

Labels

#g-auto-patchingProduct group focused on auto patching software~backendBackend-related issue.~sub-taskA technical sub-task that is part of a story. (Not QA'd. Not estimated.)

Type

Fields

No fields configured for Task.

Projects

Status
🐣 In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions