From a4dd2f1d433bc12921d8b5c2885e440e00afc705 Mon Sep 17 00:00:00 2001 From: Jiri Semmler Date: Fri, 12 Sep 2025 11:01:56 +0200 Subject: [PATCH 1/4] PAT-251 command to bulk unlink --- README.md | 23 +++++++ cli.php | 2 + .../Command/ForceUnlinkSharedBuckets.php | 62 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php diff --git a/README.md b/README.md index 77ec19c..91a3670 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,28 @@ Command execution: cat data.csv | php cli.php storage:notify-projects MANAGETOKEN ``` + +### Force Unlink Shared and Linked Buckets + +List all buckets in the project and force-unlink those that are both shared and linked. By default, the command runs in dry-run mode and only reports what would be unlinked. Use the `--force` flag to actually perform the unlinking. + +``` +php cli.php storage:force-unlink-shared-buckets [--force|-f] +``` +Arguments: +- `storageToken` (required): Storage API token for the target project. +- `url` (required): Stack URL, including `https://`. + +Options: +- `--force` / `-f`: Actually perform the unlinking. Without this flag, the command only reports what would be unlinked (dry-run). + +Behavior: +- Lists all buckets in the project. +- For each bucket, checks if it is both shared and linked. +- In dry-run mode, lists the buckets that would be unlinked. +- With `--force`, unlinks each shared and linked bucket and confirms the action. +- Prints a summary of unlinked or would-be-unlinked buckets. + ### Mass enablement of dynamic backends for multiple projects Prerequisities: https://keboola.atlassian.net/wiki/spaces/KB/pages/2135982081/Enable+Dynamic+Backends#Enable-for-project @@ -433,6 +455,7 @@ Behavior: - If the user is a member, logs removal (and performs it if forced). - Prints final count of affected projects. + # License MIT licensed, see [LICENSE](./LICENSE) file. diff --git a/cli.php b/cli.php index 09469b3..66d9339 100644 --- a/cli.php +++ b/cli.php @@ -29,6 +29,7 @@ use Symfony\Component\Console\Application; use Keboola\Console\Command\SetDataRetention; use Keboola\Console\Command\UpdateDataRetention; +use Keboola\Console\Command\ForceUnlinkSharedBuckets; $application = new Application(); $application->add(new ProjectsAddFeature()); @@ -55,4 +56,5 @@ $application->add(new MassDeleteProjectWorkspaces()); $application->add(new UpdateDataRetention()); $application->add(new OrganizationResetWorkspacePasswords()); +$application->add(new ForceUnlinkSharedBuckets()); $application->run(); diff --git a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php new file mode 100644 index 0000000..fa6a7a4 --- /dev/null +++ b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php @@ -0,0 +1,62 @@ +setName('storage:force-unlink-shared-buckets') + ->setDescription('List all buckets in the project and force-unlink those that are shared and linked.') + ->addArgument('storageToken', InputArgument::REQUIRED, 'Keboola Storage API token to use') + ->addArgument('url', InputArgument::REQUIRED, 'stack URL. Including https://') + ->addOption('force', 'f', InputOption::VALUE_NONE, 'Use [--force, -f] to actually unlink. Otherwise, dry-run.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $token = $input->getArgument('storageToken'); + $url = $input->getArgument('url'); + $isForce = $input->getOption('force'); + $prefix = $isForce ? 'FORCE: ' : 'DRY-RUN: '; + + $client = new StorageApiClient([ + 'token' => $token, + 'url' => $url, + ]); + + $buckets = $client->listBuckets(['include' => 'linkedBuckets']); + + foreach ($buckets as $bucket) { + if (array_key_exists('linkedBy', $bucket)) { + foreach ($bucket['linkedBy'] as $link) { + if ($isForce) { + $client->forceUnlinkBucket($bucket['id'], $link['project']['id']); + } + $output->writeln( + sprintf( + '%s bucket "%s" force unlinked from project "%s" (%s)', + $prefix, + $bucket['id'], + $link['project']['name'], + $link['project']['id'], + ) + ); + } + } else { + $output->writeln(\sprintf('No linked buckets found for bucket "%s"', $bucket['id'])); + } + } + + return 0; + } +} + From ae6bbaa481737ba89cf055a637dfed164fd8dc43 Mon Sep 17 00:00:00 2001 From: Jiri Semmler Date: Fri, 12 Sep 2025 11:03:34 +0200 Subject: [PATCH 2/4] cs --- src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php index fa6a7a4..099376a 100644 --- a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php +++ b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php @@ -59,4 +59,3 @@ protected function execute(InputInterface $input, OutputInterface $output) return 0; } } - From 586084cbe63d1c7943eff1ae4f08c7491b2971f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Semmler?= <13363655+jirkasemmler@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:15:26 +0200 Subject: [PATCH 3/4] Update src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php index 099376a..31373ee 100644 --- a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php +++ b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php @@ -15,7 +15,7 @@ protected function configure() { $this ->setName('storage:force-unlink-shared-buckets') - ->setDescription('List all buckets in the project and force-unlink those that are shared and linked.') + ->setDescription('List all buckets in the project and force-unlink those that are shared BY this project and linked TO other projects.') ->addArgument('storageToken', InputArgument::REQUIRED, 'Keboola Storage API token to use') ->addArgument('url', InputArgument::REQUIRED, 'stack URL. Including https://') ->addOption('force', 'f', InputOption::VALUE_NONE, 'Use [--force, -f] to actually unlink. Otherwise, dry-run.'); From e1a049ecb3484adb38d1d7eae29f400798413d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Semmler?= <13363655+jirkasemmler@users.noreply.github.com> Date: Fri, 12 Sep 2025 11:15:41 +0200 Subject: [PATCH 4/4] Update src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php index 31373ee..9fcb402 100644 --- a/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php +++ b/src/Keboola/Console/Command/ForceUnlinkSharedBuckets.php @@ -52,7 +52,7 @@ protected function execute(InputInterface $input, OutputInterface $output) ); } } else { - $output->writeln(\sprintf('No linked buckets found for bucket "%s"', $bucket['id'])); + $output->writeln(sprintf('No linked buckets found for bucket "%s"', $bucket['id'])); } }