Skip to content

Add workshop identity to workshopctl requests - #1017

Merged
dmitry-lyfar merged 7 commits into
secrets-implementationfrom
system-sdk-secret-provider
Sep 7, 2026
Merged

Add workshop identity to workshopctl requests#1017
dmitry-lyfar merged 7 commits into
secrets-implementationfrom
system-sdk-secret-provider

Conversation

@tlm

@tlm tlm commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

Lay the groundwork for associating workshopctl requests with the workshop instance from which they originated.

Add user-scoped project lookup to workshop backends and expose the LXD instance UUID on loaded workshop metadata. The UUID is normalized to match the value written to /etc/machine-id.

Add request middleware that copies the optional workshop-instance-id header into the request context, and apply it to the workshopctl endpoint.

This change only propagates workshop identity. Validation of the supplied instance ID will be added separately.

LXD integration test identity

Update the LXD integration fixture to use the test runner's numeric UID and GID rather than assuming both are 1000. Workshop instance configuration uses these values in raw.idmap to map the owning host user to the fixed workshop user inside the container.

The previous fixture worked only on hosts where the test runner happened to use UID and GID 1000. On other hosts, those IDs can belong to different accounts or groups, causing LXD container creation to fail with operation not permitted. Using user.Current() makes the mapping represent the user actually running the test while retaining the synthetic testuser name and isolated LXD project. This is a test portability fix and does not change production identity handling.

Testing

  • go test ./internal/workshop/fakebackend ./internal/workshop/lxd ./internal/overlord/workshopstate
  • go test ./internal/daemon -check.f TestWithWorkshopInstanceID
  • go test -v -tags integration ./internal/workshop/lxd/tests/integration -check.v -check.f TestLxdBackendWorkshopInstanceID

Docs

  • I confirm the PR has no implications for documentation.

@tlm
tlm requested a review from dmitry-lyfar September 3, 2026 03:35
@tlm tlm self-assigned this Sep 3, 2026
@dmitry-lyfar

Copy link
Copy Markdown
Collaborator

@tlm I can review it, but I think the tests are failing

@tlm tlm mentioned this pull request Sep 4, 2026
10 tasks
tlm added 7 commits September 4, 2026 03:40
Expose UserProjects on the backend interface so callers can retrieve only the projects owned by the user in context without listing every user's projects.

Reuse the new method in the LXD and fake backend implementations and return an empty result when the context does not identify a user.
Add middleware that copies an optional workshop instance ID header into the request context for downstream handlers.

Define a shared typed context key and cover requests with and without the header while ensuring the next response function is always called.
Derive the test user's UID and GID from the user running the suite instead of assuming both are 1000.

This allows LXD raw ID mapping to work on hosts where the test runner uses different numeric user and group IDs.
Add an instance ID to workshop runtime metadata and populate it from LXD's volatile UUID, normalized to match the value written to /etc/machine-id.

Cover the mapping through the LXD backend integration suite.
Apply the workshop instance ID middleware to the workshopctl endpoint so a supplied instance ID is available to downstream request handling through the request context.
Use the invoking user's IDs for local integration runs, but retain the non-root workshop IDs when Spread executes the suite as root. This prevents LXD from trying to map host root into an unprivileged workshop instance.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new integration test can produce false positives without asserting that LXD’s volatile.uuid is present/non-empty before comparing normalized IDs.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR propagates a workshop “instance identity” through the system so workshopctl requests can be associated with the workshop instance they originated from, laying groundwork for later validation and routing decisions.

Changes:

  • Add InstanceID to workshop.Workshop, derived from LXD’s volatile.uuid normalized to the machine-id format.
  • Add UserProjects(ctx) to the backend interface and use it for user-scoped project lookup in both LXD and fake backends.
  • Introduce daemon middleware to copy the optional workshop-instance-id header into request context and apply it to the /v1/workshopctl endpoint; update integration fixtures for UID/GID portability.
File summaries
File Description
internal/workshop/workshop.go Adds Workshop.InstanceID field for propagating backend instance identity.
internal/workshop/lxd/tests/integration/workshop_test.go Makes integration fixture UID/GID portable; adds an integration test for the exposed LXD-derived instance ID.
internal/workshop/lxd/lxd_backend.go Derives and populates Workshop.InstanceID from LXD volatile.uuid (normalized).
internal/workshop/lxd/lxd_backend_project.go Introduces UserProjects and refactors project listing to use it.
internal/workshop/lxd/lxd_backend_dns.go Switches DNS CNAME generation to use UserProjects.
internal/workshop/fakebackend/backend.go Implements UserProjects and reuses it from Projects.
internal/workshop/backend.go Adds ContextWorkshopInstanceID and extends the backend interface with UserProjects.
internal/daemon/snapshot-ingredients.yaml Includes Workshop.InstanceID in snapshot ingredient definitions.
internal/daemon/api.go Wraps /v1/workshopctl POST handler with the new instance-id middleware.
internal/daemon/api_request.go Adds middleware to copy workshop-instance-id header into request context.
internal/daemon/api_request_test.go Adds unit tests validating middleware behavior with/without the header.
Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +269 to +270
expected := strings.ReplaceAll(inst.Config["volatile.uuid"], "-", "")

func (s *Backend) userProjects(ctx context.Context) ([]workshop.Project, error) {
func (s *Backend) UserProjects(ctx context.Context) ([]workshop.Project, error) {
if _, ok := ctx.Value(workshop.ContextUser).(string); !ok {
return nil, nil

@dmitry-lyfar dmitry-lyfar Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, nil
return []workshop.Project{}, nil

The interface's comment says it would return an empty slice, though, it's probably equivalent behavior here. I'm guessing the difference will show up if this would make it as a return value for /v1/projects (not quite likely either given it would be transformed into an API level struct).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be inclined to leave it as is. len(nil) still returns 0 and append operations still work. i.e nill is safe value for slices in go.

@dmitry-lyfar
dmitry-lyfar merged commit bceb92e into secrets-implementation Sep 7, 2026
42 of 45 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.

3 participants