---
id: w6/m4/t004
title: Implement postgres.WorkspacePurger + keyvalue.WorkspacePurger
worker: worker6
status: done
estimate: 45m
depends_on: [w6/m4/t001, w6/m4/t002]
---

## Objective

Implement concrete `workspaces.WorkspacePurger`s for managed Postgres and managed KeyValue, so deleting a workspace actually deletes every `Database`/`KeyValue` CR it owns instead of leaving them orphaned.

## Context

- `lego/backend/internal/workspaces/service.go:107-113` — the `WorkspacePurger` interface: `PurgeWorkspace(ctx context.Context, tenantID string) error`.
- Depends on t001/t002 landing first: the purger needs a reliable `core.LabelTenant` label on `Database`/`KeyValue` CRs to find "every resource owned by tenant X" via `client.MatchingLabels`.
- `lego/backend/internal/postgres/service.go` and `lego/backend/internal/keyvalue/service.go` already have `s.Client` (the controller-runtime client) available for list/delete.

## Steps

1. Add a `WorkspacePurger` type in the `postgres` package: lists `Database` CRs matching `client.MatchingLabels{core.LabelTenant: tenantID}` in the service namespace, deletes each (cascades CNPG Cluster/PVC/Secret via owner refs, same as `DeletePostgres`).
2. Add the equivalent `WorkspacePurger` type in the `keyvalue` package for `KeyValue` CRs.
3. Both idempotent: an empty match list is a no-op, not an error.

## Files

- `lego/backend/internal/postgres/service.go` (or a new `purger.go`)
- `lego/backend/internal/keyvalue/service.go` (or a new `purger.go`)

## Acceptance criteria

- [ ] `postgres.WorkspacePurger` deletes every `Database` CR labeled with the given tenant id
- [ ] `keyvalue.WorkspacePurger` deletes every `KeyValue` CR labeled with the given tenant id
- [ ] Both are no-ops (not errors) when the tenant owns no resources of that type

## Out of scope

- Wiring into `ServerDeps.WorkspacePurgers` (t005).
