nacx commented on this pull request.
Many thanks @jmspring!
This looks quite good! I've added some general comments. Apart from those,
could you paste the summary of the live tests results?
> @@ -53,6 +53,33 @@ Verify service principal
az login -u <Application-id> -p <password> --service-principal --tenant
<Tenant-id>
```
+## KeyVault Live Tests
+
+In order to run the KeyVault live tests, an additional parameter is needed.
This is the `objectId` of
+the service principal being used for the tests. One can retrieve the service
principal `objectId` by
+doing the following using the Azure 2.0 CLI:
Is this property actually needed? Or are now the tests using the existing
service principal supplier to automatically get the `objectId`?
> @@ -252,6 +253,15 @@ NetworkSecurityRuleApi
> getNetworkSecurityRuleApi(@PathParam("resourcegroup") Str
@Delegate
GraphRBACApi getGraphRBACApi();
+ /**
+ * Managing your key vaults as well as the keys, secrets, and certificates
within your key vaults can be
+ * accomplished through a REST interface.+
[minor] Remove the trailing `+`.
> +import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class VaultProperties {
+
+ @AutoValue
+ public abstract static class Permissions {
+
+ @Nullable public abstract List<String> certificates();
+ @Nullable public abstract List<String> keys();
+ @Nullable public abstract List<String> secrets();
+ @Nullable public abstract List<String> storage();
These lists are never null since you enforce immutability and presence. Let's
remove the `@Nullable` annotation and apply the same pattern for all other
collections where you enforce presence.
> + @Fallback(NullOnNotFoundOr404.class)
+ @OAuthResource("https://vault.azure.net")
+ DeletedKeyBundle deleteKey(@EndpointParam URI vaultBaseUrl,
@PathParam("keyName") String keyName);
+
+ @Named("key:get_versions")
+ @GET
+ @SelectJson("value")
+ @Path("/keys/{keyName}/versions")
+ @Fallback(EmptyListOnNotFoundOr404.class)
+ @OAuthResource("https://vault.azure.net")
+ List<Key> getKeyVersions(@EndpointParam URI vaultBaseUrl,
@PathParam("keyName") String keyName);
+
+ @Named("key:update")
+ @PATCH
+ @MapBinder(BindToJsonPayload.class)
+ @Path("/keys/{keyName}{keyVersion}")
Shouldn't this be `{keyName}/{keyVersion}`?
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.functions;
+import com.google.common.base.Function;
+import org.jclouds.http.HttpResponse;
+
+import javax.inject.Singleton;
+
+import static org.jclouds.http.HttpUtils.releasePayload;
+/**
+ * Parses an http response code from http responser
+ */
+@Singleton
+public class TrueOn20X implements Function<HttpResponse, Boolean> {
Is this class actually used?
> +import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+
+import javax.inject.Inject;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+
+@Test(groups = "live", testName = "VaultApiLiveTest")
+public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
+ @Inject private Supplier<ServicePrincipal> svcPrincipal;
Injection does not work in tests. Remove this and get it from the injector in
the base test class if needed.
> + private static String cryptoText = "R29sZCUyNTIxJTJCR29sZCUyNTIxJTJCR2" +
+ "9sZCUyQmZyb20lMkJ0aGUlMkJBbWVyaWNhbiUyQlJpdmVyJTI1MjE";
+ private static String cryptoAlgorithm = "RSA-OAEP";
+ private static String hashToSign =
"FvabKT6qGwpml59iHUJ72DZ4XyJcJ8bgpgFA4_8JFmM";
+ private static String signatureAlgorithm = "RS256";
+ private static String contentEncryptionKey =
"YxzoHR65aFwD2_IOiZ5rD08jMSALA1y7b_yYW0G3hyI";
+
+ @BeforeClass
+ @Override
+ public void setup() {
+ super.setup();
+ createTestResourceGroup();
+ vaultName = String.format("kv%s",
this.getClass().getSimpleName().toLowerCase());
+ }
+
+ @AfterClass
`alwaysRun = true` to cleanup stuff even if tests fail.
> + return;
+ }
+ }
+
+ DeletedVault deletedVault = api().getDeletedVault(LOCATION, vaultName);
+ if (deletedVault != null) {
+ api().purgeVault(LOCATION, vaultName);
+ waitForOperation("vault", "purge", vaultName);
+ }
+ }
+
+ // Note: Operations on a KeyVault that supports soft delete can take time
+ // to settle. Thus for tests where appropriate, a wait operation with a
+ // one minute timeout will be used to make sure things are in the desired
+ // state before proceeding to the next test.
+ private boolean waitForOperation(String itemType, String operation, String
entityName) {
jclouds already provides clean means to actively wait for things, and allows to
configure the timeouts, etc, via properties. Please use the `Predicates2.retry`
helper instead of manually doing this.
This logic might be useful for users too, so I'd strongly recommend to copy how
[we configure active waits for Azure
ARM](https://github.com/jclouds/jclouds-labs/blob/master/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java#L145-L385)
and apply the same pattern here:
1. Refactor each conditional in this method into its own `Predicate` class.
2. Put all those predicates in an `AzurePredicatesModule` (or similar), and
consider moving the ones that already exist (linked above) to that class.
3. For each defined predicate class, add a `@Provides` method that exposes the
predicate encapsulated with the `retry` helper. Consider using the standard
jclouds timeout properties or just create Azure-specific properties to
configure them if that makes sense, but follow the same pattern.
4. [Initialize the predicates in the base test
class](https://github.com/jclouds/jclouds-labs/blob/master/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java#L106-L112)
so you can easily use them in the tests.
> + if (i == 0) {
+ break;
+ }
+ try {
+ Thread.sleep(6000);
+ } catch (java.lang.InterruptedException ie) {
+ break;
+ }
+ }
+ }
+
+ return done;
+ }
+
+ @Test
+ @Inject
Remove all `@Inject` annotations from tests.
> + ),
+ ImmutableList.<String>of()
+ ))))
+ .build(),
+ null);
+ vaultUri = vault.properties().vaultUri();
+ assertTrue(!vault.name().isEmpty());
+ }
+
+ @Test(dependsOnMethods = "testCreate")
+ public void testGet() {
+ Vault vaultFound = api().getVault(vaultName);
+ assertTrue(!vaultFound.name().isEmpty());
+ }
+
+ @Test(dependsOnMethods = "testGet")
Better depend on the `testCreate` to maximize the chances of this test being
run.
> + "Backup",
+ "Restore",
+ "Purge"
+ ),
+ ImmutableList.<String>of()
+ ))))
+ .build(),
+ null);
+ vaultUri = vault.properties().vaultUri();
+ assertTrue(!vault.name().isEmpty());
+ }
+
+ @Test(dependsOnMethods = "testCreate")
+ public void testGet() {
+ Vault vaultFound = api().getVault(vaultName);
+ assertTrue(!vaultFound.name().isEmpty());
Better `assertNotNull(vaultFound)` to avoid an unexpected NPE?
> + vaultUri = vault.properties().vaultUri();
+ assertTrue(!vault.name().isEmpty());
+ }
+
+ @Test(dependsOnMethods = "testCreate")
+ public void testGet() {
+ Vault vaultFound = api().getVault(vaultName);
+ assertTrue(!vaultFound.name().isEmpty());
+ }
+
+ @Test(dependsOnMethods = "testGet")
+ public void testList() {
+ for (Vault vault : api().listVaults()) {
+ assertNotNull(vault.name());
+ VaultProperties props = vault.properties();
+ assertNotNull(props);
If properties are not nullable there is no need to check this. Deserialization
will already fail.
> +
+ Map<String, String> tags = new HashMap<String, String>();
+ tags.put("purpose", "testing again");
+ KeyBundle updatedKey = api().updateKey(vaultUri, KEY_NAME, "/" +
version, null, null, tags);
+ assertNotNull(updatedKey);
+
+ keys = api().getKeyVersions(vaultUri, KEY_NAME);
+ assertNotNull(keys);
+ boolean found = false;
+ for (Key k : keys) {
+ if (k.kid().contains(version)) {
+ key = k;
+ found = true;
+ break;
+ }
+ }
style minor:
```java
assertTrue(Iterables.anyMatch(keys, new Predicate<Key>() {
@Override boolean apply(Key input) {
return input.kid().contains(version);
}
}));
```
> + assertEquals(certBundle.tags().size(), 1);
+ }
+
+ @Test(dependsOnMethods = "testUpdateCertificate")
+ public void testUpdateCertificateVersion() {
+ // create a new version of the certificate
+ /*
+ * XXX -- update using version complains about needing policy (required
input), yet
+ * passing in the same policy results in the error:
+ *
+ * Policy cannot be updated with a specific version of a certificate
+ *
+ * Will uncomment/fix once this issue is resolved.
+ *
+ */
+ assertTrue(true);
Better throw a SkipException to see the reason why this is not executed in the
test output. this will also serve as a reminder for us that this needs to be
implemented.
> +
> "A6RII-HF2BkBcVa9KcAX3_di4KQE70PXgHf-dlz_RgLOJILeG50wzFeBFCLsjEEPp3itmoai" +
+
"E6vfDidCRm5At8Vjka0G-N_afwkIijfQZLT0VaXvL39cIJE2QN3HJPZM8YPUlkFlYnY4GIRy" +
+
"RWSBpK_KYuVufzUGtDi6Sh8pUa67ppa7DHVZlixlmnVqI3Oeg6XUvMqbFFqVSrcNbRQDwVGL" +
+ "3cUtK-KB1PfKg";
+ private static String keySignedData =
"uO0r4P1cB-fKsDZ8cj5ahiNw8Tdsudt5zLCeEKOt29" +
+
"LAlPDpeGx9Q1SOFNaR7JlRYVelxsohdzvydwX8ao6MLnqlpdEj0Xt5Aadp-kN84AXW238gab" +
+
"S1AUyiWILCmdsBFeRU4wTRSxz2qGS_0ztHkaNln32P_9GJC72ZRlgZoVA4C_fowZolUoCWGj" +
+
"4V7fAzcSoiNYipWP0HkFe3xmuz-cSQg3CCAs-MclHHfMeSagLJZZQ9bpl5LIr-Ik89bNtqEq" +
+
"yP7Jb_fCgHajAx2lUFcRZhSIKuCfrLPMl6wzejQ2rQXX-ixEkDa73dYaPIrVW4IL3iC0Ufxn" +
+ "fxYffHJ7QCRw";
+ private static String keyWrappedData =
"1jcTlu3KJNDBYydhaH9POWOo0tAPGkpsZVizCkHpC" +
+
"3g_9Kg91Q3HKK-rfZynn5W5nVPM-SVFHA3JTankcXX8gx8GycwUh4pMoyil_DV35m2QjyuiT" +
+
"ln83OJXw-nMvRXyKdVfF7nyRcs256kW7gthAOsYUVBrfFS7DFFxsXqLNREsA8j85IqIXIm8p" +
+
"AB3C9uvl1I7SQhLvrwZZXXqjeCWMfseVJwWgsQFyyqH2P0f3-xnngV7cvik2k3Elrk3G_2Cu" +
+
"JCozIIrANg9zG9Z8DrwSNNm9YooxWkSu0ZeDLOJ0bMdhcPGGm5OvKz3oZqX-39yv5klNlCRb" +
+ "r0q7gqmI0x25w";
Consider moving all these huge strings and the ones in the live test to files
in `src/test/resources` for better readability.
> +
> "4V7fAzcSoiNYipWP0HkFe3xmuz-cSQg3CCAs-MclHHfMeSagLJZZQ9bpl5LIr-Ik89bNtqEq" +
+
"yP7Jb_fCgHajAx2lUFcRZhSIKuCfrLPMl6wzejQ2rQXX-ixEkDa73dYaPIrVW4IL3iC0Ufxn" +
+ "fxYffHJ7QCRw";
+ private static String keyWrappedData =
"1jcTlu3KJNDBYydhaH9POWOo0tAPGkpsZVizCkHpC" +
+
"3g_9Kg91Q3HKK-rfZynn5W5nVPM-SVFHA3JTankcXX8gx8GycwUh4pMoyil_DV35m2QjyuiT" +
+
"ln83OJXw-nMvRXyKdVfF7nyRcs256kW7gthAOsYUVBrfFS7DFFxsXqLNREsA8j85IqIXIm8p" +
+
"AB3C9uvl1I7SQhLvrwZZXXqjeCWMfseVJwWgsQFyyqH2P0f3-xnngV7cvik2k3Elrk3G_2Cu" +
+
"JCozIIrANg9zG9Z8DrwSNNm9YooxWkSu0ZeDLOJ0bMdhcPGGm5OvKz3oZqX-39yv5klNlCRb" +
+ "r0q7gqmI0x25w";
+
+ @BeforeMethod
+ public void start() throws IOException {
+ super.start();
+ try {
+ vaultUri = server.getUrl("").toURI();
+ } catch (URISyntaxException use) {
Better add the exception to the method signature instead of silently catching
it? This way if it is thrown tests will be skipped instead of run with a `null`
URI.
> + "Delete",
+ "Recover",
+ "Backup",
+ "Restore",
+ "Purge"
+ ),
+ ImmutableList.<String>of()
+ ))))
+ .build(),
+ null);
+
+ String path = String.format(
+
"/subscriptions/%s/resourcegroups/%s/providers/Microsoft.KeyVault/vaults/%s?%s",
+ subscriptionId, resourceGroup, vaultName, apiVersion
+ );
+ assertSent(server, "PUT", path);
I know this can be kind of a pain, but we need to assert the sent payload too
for all requests that have a body. This *really* helps future maintenance of
the different APIs.
> + ))))
+ .build(),
+ null);
+
+ String path = String.format(
+
"/subscriptions/%s/resourcegroups/%s/providers/Microsoft.KeyVault/vaults/%s?%s",
+ subscriptionId, resourceGroup, vaultName, apiVersion
+ );
+ assertSent(server, "PUT", path);
+
+ assertNotNull(vault);
+ assertNotNull(vault.properties().vaultUri());
+ assertTrue(!vault.name().isEmpty());
+ }
+
+ public void createVaultReturns404() throws InterruptedException {
There is no need to test the 404 behavior if there is no custom `@Fallback`
defined, so you can remove all the 404 tests for those methods that do not have
fallbacks. The default behavior (throwing the exception) is already tested in
jclouds-core.
> +
> "/subscriptions/%s/providers/Microsoft.KeyVault/locations/%s/deletedVaults/%s?%s",
+ subscriptionId, location, vaultName, apiVersion
+ );
+ assertSent(server, "GET", path);
+
+ assertNull(vault);
+ }
+
+
+ // Key mock tests
+ public void listKeys() throws InterruptedException {
+ server.enqueue(jsonResponse("/vaultlistkeys.json").setResponseCode(200));
+ final VaultApi vaultApi = api.getVaultApi(resourceGroup);
+ List<Key> keys = vaultApi.listKeys(vaultUri);
+
+ String path = String.format("/keys?%s", apiVersion);
Same comment about skipping the test.
> @@ -99,6 +100,7 @@ public void setup() {
vaultResourceGroup =
System.getProperty("test.azurecompute-arm.vault.resource.group");
vaultName = System.getProperty("test.azurecompute-arm.vault.name");
vaultCertificateUrl =
System.getProperty("test.azurecompute-arm.vault.certificate.url");
+ tenantId = System.getProperty("test.azurecompute-arm.tenantId");
Change to: `tenantId = injector.getInstance(Key.get(String.class,
Tenant.class));`
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/jclouds/jclouds-labs/pull/416#pullrequestreview-80448092