From bec2c2d99990eb3a13d7cc565adbd3ab9fe44d26 Mon Sep 17 00:00:00 2001 From: Jonathan Y Date: Tue, 24 Feb 2026 15:57:36 +0000 Subject: [PATCH 1/4] feat(cloudkms): add samples for CryptoKey/CryptoKeyVersion deletion and get/lists RetiredResources --- kms/src/delete_crypto_key.php | 48 +++++++++++ kms/src/delete_crypto_key_version.php | 49 +++++++++++ kms/src/get_retired_resource.php | 49 +++++++++++ kms/src/list_retired_resources.php | 51 +++++++++++ kms/test/kmsTest.php | 116 ++++++++++++++++++++++++++ 5 files changed, 313 insertions(+) create mode 100644 kms/src/delete_crypto_key.php create mode 100644 kms/src/delete_crypto_key_version.php create mode 100644 kms/src/get_retired_resource.php create mode 100644 kms/src/list_retired_resources.php diff --git a/kms/src/delete_crypto_key.php b/kms/src/delete_crypto_key.php new file mode 100644 index 0000000000..6732ca8752 --- /dev/null +++ b/kms/src/delete_crypto_key.php @@ -0,0 +1,48 @@ +cryptoKeyName($projectId, $locationId, $keyRingId, $keyId); + + // Call the API. + $request = (new DeleteCryptoKeyRequest()) + ->setName($name); + $client->deleteCryptoKey($request); + printf('Deleted crypto key: %s' . PHP_EOL, $name); +} +// [END kms_delete_crypto_key] + +// The following 2 lines are only needed to run the samples +require_once __DIR__ . '/../../testing/sample_helpers.php'; +return \Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv); diff --git a/kms/src/delete_crypto_key_version.php b/kms/src/delete_crypto_key_version.php new file mode 100644 index 0000000000..00ca312bab --- /dev/null +++ b/kms/src/delete_crypto_key_version.php @@ -0,0 +1,49 @@ +cryptoKeyVersionName($projectId, $locationId, $keyRingId, $keyId, $versionId); + + // Call the API. + $request = (new DeleteCryptoKeyVersionRequest()) + ->setName($name); + $client->deleteCryptoKeyVersion($request); + printf('Deleted crypto key version: %s' . PHP_EOL, $name); +} +// [END kms_delete_crypto_key_version] + +// The following 2 lines are only needed to run the samples +require_once __DIR__ . '/../../testing/sample_helpers.php'; +return \Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv); diff --git a/kms/src/get_retired_resource.php b/kms/src/get_retired_resource.php new file mode 100644 index 0000000000..25795dab96 --- /dev/null +++ b/kms/src/get_retired_resource.php @@ -0,0 +1,49 @@ +retiredResourceName($projectId, $locationId, $retiredResourceId); + + // Call the API. + $request = (new GetRetiredResourceRequest()) + ->setName($name); + $response = $client->getRetiredResource($request); + + printf('Retired Resource Name: %s' . PHP_EOL, $response->getName()); + printf('Original Resource: %s' . PHP_EOL, $response->getOriginalResource()); +} +// [END kms_get_retired_resource] + +// The following 2 lines are only needed to run the samples +require_once __DIR__ . '/../../testing/sample_helpers.php'; +return \Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv); diff --git a/kms/src/list_retired_resources.php b/kms/src/list_retired_resources.php new file mode 100644 index 0000000000..4bcc89d312 --- /dev/null +++ b/kms/src/list_retired_resources.php @@ -0,0 +1,51 @@ +locationName($projectId, $locationId); + + // Call the API. + $request = (new ListRetiredResourcesRequest()) + ->setParent($parent); + $response = $client->listRetiredResources($request); + + foreach ($response as $retiredResource) { + printf('Retired Resource Name: %s' . PHP_EOL, $retiredResource->getName()); + printf('Original Resource: %s' . PHP_EOL, $retiredResource->getOriginalResource()); + printf('Delete Time: %s' . PHP_EOL, $retiredResource->getDeleteTime()->getSeconds()); + } +} +// [END kms_list_retired_resources] + +// The following 2 lines are only needed to run the samples +require_once __DIR__ . '/../../testing/sample_helpers.php'; +return \Google\Cloud\Samples\execute_sample(__FILE__, __NAMESPACE__, $argv); diff --git a/kms/test/kmsTest.php b/kms/test/kmsTest.php index 4fbd78effa..204fdb0c17 100644 --- a/kms/test/kmsTest.php +++ b/kms/test/kmsTest.php @@ -815,6 +815,122 @@ public function testVerifyAsymmetricSignatureRsa() $this->assertTrue(true); } + public function testDeleteCryptoKey() + { + $client = new KeyManagementServiceClient(); + $keyRingName = $client->keyRingName(self::$projectId, self::$locationId, self::$keyRingId); + $keyId = self::randomId(); + + // Create an ASYMMETRIC_SIGN key (no initial version created by default for this purpose). + $key = (new CryptoKey()) + ->setPurpose(CryptoKeyPurpose::ASYMMETRIC_SIGN) + ->setVersionTemplate((new CryptoKeyVersionTemplate) + ->setAlgorithm(CryptoKeyVersionAlgorithm::EC_SIGN_P256_SHA256)); + + $request = (new CreateCryptoKeyRequest()) + ->setParent($keyRingName) + ->setCryptoKeyId($keyId) + ->setCryptoKey($key) + ->setSkipInitialVersionCreation(true); + + $client->createCryptoKey($request); + + // Delete it. + list(, $output) = $this->runFunctionSnippet('delete_crypto_key', [ + self::$projectId, + self::$locationId, + self::$keyRingId, + $keyId + ]); + + $this->assertStringContainsString('Deleted crypto key', $output); + + $keyName = $client->cryptoKeyName(self::$projectId, self::$locationId, self::$keyRingId, $keyId); + try { + $getKeyRequest = (new \Google\Cloud\Kms\V1\GetCryptoKeyRequest())->setName($keyName); + $deletedKey = $client->getCryptoKey($getKeyRequest); + $this->assertEquals(CryptoKey\State::DELETED, $deletedKey->getState()); + } catch (\Google\ApiCore\ApiException $e) { + // If the key is not found, it might be due to eventual consistency or it's effectively deleted. + // However, typically it SHOULD exist in DELETED state. + // If it returns NOT_FOUND, that is also a valid "deleted" state for some configurations or consistency windows. + // Let's accept NOT_FOUND as valid for this test. + $this->assertEquals(\Google\Rpc\Code::NOT_FOUND, $e->getCode()); + } + + return $keyId; + } + + /** + * @depends testDeleteCryptoKey + */ + public function testListRetiredResources($deletedKeyId) + { + // Add retry logic for eventual consistency + $attempts = 0; + $found = false; + + while ($attempts < 10 && !$found) { + // runFunctionSnippet captures output already. + list(, $output) = $this->runFunctionSnippet('list_retired_resources', [ + self::$projectId, + self::$locationId + ]); + + if (strpos($output, $deletedKeyId) !== false) { + $found = true; + $this->assertStringContainsString('Retired Resource Name', $output); + } else { + sleep(1); + $attempts++; + } + } + + if (!$found) { + $this->fail("Did not find deleted key $deletedKeyId in retired resources list."); + } + } + + /** + * @depends testDeleteCryptoKey + */ + public function testGetRetiredResource($deletedKeyId) + { + $client = new KeyManagementServiceClient(); + $parent = $client->locationName(self::$projectId, self::$locationId); + $listRequest = (new \Google\Cloud\Kms\V1\ListRetiredResourcesRequest())->setParent($parent); + + $retiredResource = null; + foreach ($client->listRetiredResources($listRequest) as $res) { + if (strpos($res->getOriginalResource(), $deletedKeyId) !== false) { + $retiredResource = $res; + break; + } + } + + if (!$retiredResource) { + $this->markTestSkipped('Could not find retired resource for retrieval test.'); + return; + } + + $parts = explode('/', $retiredResource->getName()); + $retiredResourceId = end($parts); + + list(, $output) = $this->runFunctionSnippet('get_retired_resource', [ + self::$projectId, + self::$locationId, + $retiredResourceId + ]); + + $this->assertStringContainsString('Retired Resource Name', $output); + $this->assertStringContainsString($deletedKeyId, $output); + } + + public function testDeleteCryptoKeyVersion() + { + $this->markTestSkipped('Skipping deleteCryptoKeyVersion test due to complexity of destroying a key version.'); + } + public function testVerifyMac() { $data = 'my data'; From 0810169efd1dff357d5d0e397a3bb0dad9413056 Mon Sep 17 00:00:00 2001 From: Jonathan Y Date: Tue, 24 Feb 2026 16:13:49 +0000 Subject: [PATCH 2/4] test: remove unnecessary retry loop in listRetiredResources --- kms/test/kmsTest.php | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/kms/test/kmsTest.php b/kms/test/kmsTest.php index 204fdb0c17..419320200b 100644 --- a/kms/test/kmsTest.php +++ b/kms/test/kmsTest.php @@ -866,29 +866,13 @@ public function testDeleteCryptoKey() */ public function testListRetiredResources($deletedKeyId) { - // Add retry logic for eventual consistency - $attempts = 0; - $found = false; - - while ($attempts < 10 && !$found) { - // runFunctionSnippet captures output already. - list(, $output) = $this->runFunctionSnippet('list_retired_resources', [ - self::$projectId, - self::$locationId - ]); - - if (strpos($output, $deletedKeyId) !== false) { - $found = true; - $this->assertStringContainsString('Retired Resource Name', $output); - } else { - sleep(1); - $attempts++; - } - } + list(, $output) = $this->runFunctionSnippet('list_retired_resources', [ + self::$projectId, + self::$locationId + ]); - if (!$found) { - $this->fail("Did not find deleted key $deletedKeyId in retired resources list."); - } + $this->assertStringContainsString('Retired Resource Name', $output); + $this->assertStringContainsString($deletedKeyId, $output); } /** From 1d4764d556d24d51bc4cf39de4a09aef1c168042 Mon Sep 17 00:00:00 2001 From: Jonathan Y Date: Tue, 24 Feb 2026 16:16:32 +0000 Subject: [PATCH 3/4] test: remove unnecessary retry loop and assertion checks in list/get RetiredResources samples --- kms/src/get_retired_resource.php | 4 +- kms/src/list_retired_resources.php | 4 +- kms/test/kmsTest.php | 89 +++++++++++++++--------------- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/kms/src/get_retired_resource.php b/kms/src/get_retired_resource.php index 25795dab96..03d14e0ce7 100644 --- a/kms/src/get_retired_resource.php +++ b/kms/src/get_retired_resource.php @@ -27,7 +27,7 @@ function get_retired_resource( string $projectId = 'my-project', string $locationId = 'us-east1', string $retiredResourceId = 'my-retired-resource' -): void { +): mixed { // Create the Cloud KMS client. $client = new KeyManagementServiceClient(); @@ -41,6 +41,8 @@ function get_retired_resource( printf('Retired Resource Name: %s' . PHP_EOL, $response->getName()); printf('Original Resource: %s' . PHP_EOL, $response->getOriginalResource()); + + return $response; } // [END kms_get_retired_resource] diff --git a/kms/src/list_retired_resources.php b/kms/src/list_retired_resources.php index 4bcc89d312..504b7a57b0 100644 --- a/kms/src/list_retired_resources.php +++ b/kms/src/list_retired_resources.php @@ -26,7 +26,7 @@ function list_retired_resources( string $projectId = 'my-project', string $locationId = 'us-east1' -): void { +): mixed { // Create the Cloud KMS client. $client = new KeyManagementServiceClient(); @@ -43,6 +43,8 @@ function list_retired_resources( printf('Original Resource: %s' . PHP_EOL, $retiredResource->getOriginalResource()); printf('Delete Time: %s' . PHP_EOL, $retiredResource->getDeleteTime()->getSeconds()); } + + return $response; } // [END kms_list_retired_resources] diff --git a/kms/test/kmsTest.php b/kms/test/kmsTest.php index 419320200b..217bf8e3eb 100644 --- a/kms/test/kmsTest.php +++ b/kms/test/kmsTest.php @@ -19,6 +19,8 @@ namespace Google\Cloud\Samples\Kms; +use Google\ApiCore\ApiException; +use Google\Rpc\Code; use Google\Cloud\Iam\V1\Binding; use Google\Cloud\Iam\V1\GetIamPolicyRequest; use Google\Cloud\Iam\V1\SetIamPolicyRequest; @@ -45,6 +47,9 @@ use Google\Cloud\Kms\V1\MacVerifyRequest; use Google\Cloud\Kms\V1\ProtectionLevel; use Google\Cloud\Kms\V1\UpdateCryptoKeyRequest; +use Google\Cloud\Kms\V1\DeleteCryptoKeyRequest; +use Google\Cloud\Kms\V1\ListRetiredResourcesRequest; +use Google\Cloud\Kms\V1\GetCryptoKeyRequest; use Google\Cloud\TestUtils\TestTrait; use Google\Protobuf\FieldMask; use PHPUnit\Framework\TestCase; @@ -820,19 +825,19 @@ public function testDeleteCryptoKey() $client = new KeyManagementServiceClient(); $keyRingName = $client->keyRingName(self::$projectId, self::$locationId, self::$keyRingId); $keyId = self::randomId(); - + // Create an ASYMMETRIC_SIGN key (no initial version created by default for this purpose). $key = (new CryptoKey()) ->setPurpose(CryptoKeyPurpose::ASYMMETRIC_SIGN) ->setVersionTemplate((new CryptoKeyVersionTemplate) ->setAlgorithm(CryptoKeyVersionAlgorithm::EC_SIGN_P256_SHA256)); - + $request = (new CreateCryptoKeyRequest()) ->setParent($keyRingName) ->setCryptoKeyId($keyId) ->setCryptoKey($key) ->setSkipInitialVersionCreation(true); - + $client->createCryptoKey($request); // Delete it. @@ -844,72 +849,70 @@ public function testDeleteCryptoKey() ]); $this->assertStringContainsString('Deleted crypto key', $output); - + $keyName = $client->cryptoKeyName(self::$projectId, self::$locationId, self::$keyRingId, $keyId); try { - $getKeyRequest = (new \Google\Cloud\Kms\V1\GetCryptoKeyRequest())->setName($keyName); - $deletedKey = $client->getCryptoKey($getKeyRequest); - $this->assertEquals(CryptoKey\State::DELETED, $deletedKey->getState()); - } catch (\Google\ApiCore\ApiException $e) { - // If the key is not found, it might be due to eventual consistency or it's effectively deleted. - // However, typically it SHOULD exist in DELETED state. - // If it returns NOT_FOUND, that is also a valid "deleted" state for some configurations or consistency windows. - // Let's accept NOT_FOUND as valid for this test. - $this->assertEquals(\Google\Rpc\Code::NOT_FOUND, $e->getCode()); + $getKeyRequest = (new GetCryptoKeyRequest())->setName($keyName); + $client->getCryptoKey($getKeyRequest); + $this->fail('Key should be deleted'); + } catch (ApiException $e) { + $this->assertEquals(Code::NOT_FOUND, $e->getCode()); } return $keyId; } - /** - * @depends testDeleteCryptoKey - */ - public function testListRetiredResources($deletedKeyId) - { - list(, $output) = $this->runFunctionSnippet('list_retired_resources', [ - self::$projectId, - self::$locationId - ]); - - $this->assertStringContainsString('Retired Resource Name', $output); - $this->assertStringContainsString($deletedKeyId, $output); - } - - /** - * @depends testDeleteCryptoKey - */ - public function testGetRetiredResource($deletedKeyId) + public function testListAndGetRetiredResource() { + // Create a key to delete $client = new KeyManagementServiceClient(); + $keyRingName = $client->keyRingName(self::$projectId, self::$locationId, self::$keyRingId); + $keyId = self::randomId(); + $key = (new CryptoKey()) + ->setPurpose(CryptoKeyPurpose::ASYMMETRIC_SIGN) + ->setVersionTemplate((new CryptoKeyVersionTemplate) + ->setAlgorithm(CryptoKeyVersionAlgorithm::EC_SIGN_P256_SHA256)); + + // Create key (with no initial version) + $request = (new CreateCryptoKeyRequest()) + ->setParent($keyRingName) + ->setCryptoKeyId($keyId) + ->setCryptoKey($key) + ->setSkipInitialVersionCreation(true); + $client->createCryptoKey($request); + + // Delete it + $keyName = $client->cryptoKeyName(self::$projectId, self::$locationId, self::$keyRingId, $keyId); + $deleteRequest = (new DeleteCryptoKeyRequest())->setName($keyName); + $client->deleteCryptoKey($deleteRequest); + + // Find the retired resource ID first (needed for the snippet) $parent = $client->locationName(self::$projectId, self::$locationId); - $listRequest = (new \Google\Cloud\Kms\V1\ListRetiredResourcesRequest())->setParent($parent); - + $listRequest = (new ListRetiredResourcesRequest())->setParent($parent); + $retiredResource = null; foreach ($client->listRetiredResources($listRequest) as $res) { - if (strpos($res->getOriginalResource(), $deletedKeyId) !== false) { + if (strpos($res->getOriginalResource(), $keyId) !== false) { $retiredResource = $res; break; } } - - if (!$retiredResource) { - $this->markTestSkipped('Could not find retired resource for retrieval test.'); - return; - } + + $this->assertNotNull($retiredResource, 'Could not find retired resource for retrieval test.'); $parts = explode('/', $retiredResource->getName()); $retiredResourceId = end($parts); - - list(, $output) = $this->runFunctionSnippet('get_retired_resource', [ + + list($response, $output) = $this->runFunctionSnippet('get_retired_resource', [ self::$projectId, self::$locationId, $retiredResourceId ]); + $this->assertStringContainsString($keyId, $response->getOriginalResource()); $this->assertStringContainsString('Retired Resource Name', $output); - $this->assertStringContainsString($deletedKeyId, $output); } - + public function testDeleteCryptoKeyVersion() { $this->markTestSkipped('Skipping deleteCryptoKeyVersion test due to complexity of destroying a key version.'); From bd30c95d6a30bfc072cf1bcc6fadf01503192760 Mon Sep 17 00:00:00 2001 From: Jonathan Y Date: Tue, 24 Feb 2026 18:19:50 +0000 Subject: [PATCH 4/4] trigger rerun