Skip to content

Add manage:delete-sandbox-workspaces command - #92

Closed
jirkasemmler wants to merge 4 commits into
mainfrom
jirka/delete-sandbox-workspaces
Closed

Add manage:delete-sandbox-workspaces command#92
jirkasemmler wants to merge 4 commits into
mainfrom
jirka/delete-sandbox-workspaces

Conversation

@jirkasemmler

Copy link
Copy Markdown
Contributor

Summary

  • New manage:delete-sandbox-workspaces CLI command that cleans up keboola.sandboxes workspaces in a project (--project-id) or across an entire organization (--organization-id).
  • For each workspace it pulls editor sessions from Editor Service and matches them by connection.schema; workspaces with an active session are skipped. The rest are deleted in the order configuration → workspace (configuration deleted twice = trash + purge, matching existing cleanup commands).
  • Scoped by a creation-date window (--created-after / --created-before, default -30 daysnow) and gated by --force (dry-run by default). Verbose per-workspace logging always on, plus per-branch / per-project / final summaries with skip-reason breakdown.

Test plan

  • php cli.php manage:delete-sandbox-workspaces --help shows the new options
  • Dry-run against a real project (--project-id) and inspect the candidate list and skip-reason counts
  • Dry-run against an organization (--organization-id)
  • Re-run with --force against a throwaway project and verify configs + workspaces disappear and active-session workspaces are untouched

🤖 Generated with Claude Code

jirkasemmler and others added 2 commits May 20, 2026 12:36
Adds a new CLI command that deletes keboola.sandboxes workspaces in a
project or organization. It pulls the workspace list from Connection,
skips any workspace whose schema still has an active session in the
Editor Service, then deletes the workspace configuration followed by
the workspace itself for the remaining ones. Scoped by --project-id or
--organization-id (mutually exclusive), filtered by a creation-date
window, with --force gating real deletions and verbose per-workspace
logging in dry-run as well.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

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.

Pull request overview

Adds a new CLI maintenance command to identify and (optionally) delete keboola.sandboxes workspaces that have no active Editor Service session, scoped by project/organization and a creation-date window.

Changes:

  • Introduces manage:delete-sandbox-workspaces with --project-id / --organization-id, --created-after / --created-before, and --force (dry-run default).
  • Implements per-project branching traversal, session-by-schema matching, and deletion order (configuration trash+purge → workspace) with summarized reporting.
  • Registers the new command in the CLI application bootstrap.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/Keboola/Console/Command/DeleteSandboxWorkspaces.php New command implementation: discovery, filtering, session matching, deletion, and reporting.
cli.php Registers the new command in the application.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +361 to +369
} catch (StorageClientException $e) {
if (str_contains($e->getMessage(), 'not found')) {
$output->writeln(sprintf(
' Configuration %s/%s already gone',
self::COMPONENT_ID,
$configurationId,
));
$configDeleted = true;
} else {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

s tím celkem souhlasím

(string) $workspaceId,
$createdStr,
));
$projectSkippedComponent++;
$editorClient = new EditorServiceClient($editorUrl, $storageToken['token']);

// Index editor sessions by workspaceSchema for O(1) lookup against workspace credentials.
$sessionsBySchema = [];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no ony mají workspaceId

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

hypoteticky muzes narazit na session, ktera WS nema, protoze se zrovna vytváí, ale nemyslím si, ze by to tady neco rozbilo

}

if ($configurationId === '') {
$output->writeln(sprintf(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no jestli jsou pro komponentu keboola.sandboxes a současně ta konfigurace neexistuje, tak bych je smazal ne? co s nima.


class DeleteSandboxWorkspaces extends Command
{
private const string COMPONENT_ID = 'keboola.sandboxes';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

a tak pojďmě přihodit ještě keboola.snowflake-transformation

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ono je asi ne zvážení jestli to nedát configurovatelný a s defaultem na keboola.sandboxes anebo chceš dělat ještě nějakej další command?

null,
InputOption::VALUE_REQUIRED,
'Only consider workspaces created at or after this date '
. '(strtotime expression, e.g. "-30 days", "2026-01-01"). Default: "-30 days".',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

to jsou takový divný defaulty - je to spíš naopak, ty měsíc starý asi jako mazat nechceš

odinuv added 2 commits May 22, 2026 16:02
- Make --component configurable (default keboola.sandboxes); empty value
  permitted so workspaces with no component can be targeted.
- Enrich SKIP/DELETE log lines and the per-project candidates listing with
  component, configurationId, loginType and owner email (from
  creatorToken.description), falling back to "(none)" when absent.
- Override the active-editor-session SKIP when the workspace points at a
  configurationId that no longer exists: GET the config, treat 404 as a
  stale session, log a NOTICE and proceed to delete the workspace.
The earlier SKIP for empty configurationId already returns, so the
nested check inside the active-session branch was unreachable as false.
jirkasemmler added a commit that referenced this pull request Jul 30, 2026
…ting

Consolidates the outstanding workspace tooling into this branch, replacing
PRs #92, #98 and #100. Adds three commands and documents them; no existing
command is modified.

manage:check-project-workspaces-state (read-only)
  Triage step for a known list of workspaces: reports whether each is still
  live and whether its configuration is live, trashed or gone, and suggests
  the follow-up command. Configuration state is resolved per branch, because
  branches hold independent copies under the same id and merging them
  misclassifies a config that is live in one branch and trashed in another.
  Also reports each configuration's most recent job from the Queue API, which
  is usually what decides whether it is still in use, and runs a sanity query
  for any job in the project first so that blank lastJob columns are not
  misread as "never used".

manage:delete-project-workspaces-by-id
  List-driven deletion with layered guards: password-login only unless
  --any-login-type, optional expected-schema verification, and configuration
  deletion only behind --with-configuration and only when that configuration
  owns exactly the listed workspace. Deletes the workspace without touching
  the configuration by default. A purge refused with
  storage.components.cannotDeleteConfiguration is counted as failed rather
  than deleted, since in that case the configuration is still in the trash and
  its workspace and backend user still exist.

manage:list-external-buckets (read-only)
  Stack-wide CSV audit of external buckets with created date, isReadOnly and
  whether KBC.description metadata is set. Drops its temporary token even when
  listing fails, and continues past projects it cannot reach.

README gains entries for all three, and the "Which command should I use?"
table now points at the by-id command as the preferred list-driven option over
manage:mass-delete-project-workspaces, which needs an interactively pasted
token per project and always purges the configuration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jirkasemmler

Copy link
Copy Markdown
Contributor Author

Closing as part of consolidating the workspace tooling into #106.

Unlike #100, this one is not carried overmanage:delete-sandbox-workspaces is not added by #106. The decision was to consolidate without introducing another component-and-age sweeper, since the repo already has two (storage:delete-orphaned-workspaces and manage:delete-organization-workspaces) plus two ownerless variants.

What that means concretely, so it is on the record rather than lost:

  • The component+age sweep use case is still served by the two existing commands. Consolidate workspace tooling: state check, delete-by-id, external buckets + docs #106 documents both properly for the first time, including that "orphaned" detects nothing and selects purely by component + age.
  • The capability that genuinely disappears is the editor-session liveness check — "delete sandbox workspaces that have no active editor session, and treat a session whose configuration is gone as stale". Nothing else does that. storage:delete-ownerless-workspaces also reads editor sessions, but keys on the owner no longer being an active user, which is a different question. If that turns out to be needed, it should come back as an option on an existing sweeper rather than as a seventh delete command.

@odinuv — your review comments here were never answered, so for the record rather than to reopen the discussion:

  • Configurable component with a keboola.sandboxes default — this was implemented after your comment (--component, defaulting to keboola.sandboxes), which is why that thread shows as outdated. It just never got a reply.
  • Adding keboola.snowflake-transformation — worth being explicit about why this one is dangerous as a default. This command deleted the parent configuration, not just the workspace. For keboola.sandboxes that is correct, because the configuration is the sandbox. For keboola.snowflake-transformation the configuration is the user's transformation code, so a component+age sweep that purges configurations is the INC-01328 mechanism. Consolidate workspace tooling: state check, delete-by-id, external buckets + docs #106 writes that distinction into the README as the first thing you read in that section.
  • Sessions have workspaceId — agreed, and matching by schema was the weaker choice. Note EditorServiceClient::listSessions() still declares only workspaceSchema in its return type, so that would have needed the client updated too.
  • The date defaults being backwards — never resolved. The window was created-after=-30 dayscreated-before=now, which targets the newest workspaces and leaves old ones alone.
  • Copilot's point about str_contains($e->getMessage(), 'not found') was right; getStringCode() is what the commands in Consolidate workspace tooling: state check, delete-by-id, external buckets + docs #106 use.

The branch stays, so this is reversible if the session-aware sweep turns out to be worth having.

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