Fix GCE live tests

This commit is contained in:
Ignasi Barrera 2017-11-29 10:36:04 +01:00
parent a0f659faed
commit 715994b125
18 changed files with 134 additions and 89 deletions

View File

@ -16,32 +16,11 @@
*/
package org.jclouds.googlecomputeengine.compute.functions;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import com.google.common.util.concurrent.Atomics;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.crypto.Crypto;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput;
import org.jclouds.googlecomputeengine.domain.Metadata;
import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.features.InstanceApi;
import org.jclouds.logging.Logger;
import org.jclouds.util.Predicates2;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Iterables.tryFind;
import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.inject.Inject;
import javax.inject.Named;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
@ -53,13 +32,42 @@ import java.security.spec.RSAPublicKeySpec;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.crypto.Crypto;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput;
import org.jclouds.googlecomputeengine.domain.Metadata;
import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.features.InstanceApi;
import org.jclouds.json.Json;
import org.jclouds.logging.Logger;
import org.jclouds.util.Predicates2;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import com.google.common.util.concurrent.Atomics;
import com.google.gson.GsonBuilder;
import com.google.inject.TypeLiteral;
/**
* References:
@ -92,12 +100,15 @@ public class ResetWindowsPassword implements Function<Map<String, ?>, String> {
private final GoogleComputeEngineApi api;
private final Crypto crypto;
private final Predicate<AtomicReference<Operation>> operationDone;
private final Json json;
@Inject
protected ResetWindowsPassword(GoogleComputeEngineApi api, Crypto crypto, Predicate<AtomicReference<Operation>> operationDone) {
protected ResetWindowsPassword(GoogleComputeEngineApi api, Crypto crypto,
Predicate<AtomicReference<Operation>> operationDone, Json json) {
this.api = api;
this.crypto = crypto;
this.operationDone = operationDone;
this.json = json;
}
@Override
@ -135,31 +146,64 @@ public class ResetWindowsPassword implements Function<Map<String, ?>, String> {
operation.get().httpErrorMessage());
}
try {
final Map<String, String> passwordDict = new HashMap<String, String>();
boolean passwordRetrieved = Predicates2.retry(new Predicate<Instance>() {
public boolean apply(Instance instance) {
String serialPortContents = instanceApi.getSerialPortOutput(instance.name(), 4).contents();
if (!serialPortContents.startsWith("{\"ready\":true")) {
return false;
}
String[] contentEntries = serialPortContents.split("\n");
passwordDict.clear();
passwordDict.putAll(new Gson().fromJson(contentEntries[contentEntries.length - 1], Map.class));
passwordDict.put("passwordDictContentEntries", contentEntries[contentEntries.length - 1]);
return passwordDict.get("encryptedPassword") != null;
}
}, 10 * 60, 30, TimeUnit.SECONDS).apply(instance.get()); // Notice that timeoutDuration should be less than EXPIRE_DURATION
if (passwordRetrieved) {
return decryptPassword(checkNotNull(passwordDict.get("encryptedPassword"), "encryptedPassword shouldn't be null"), keys);
} else {
throw new IllegalStateException("encryptedPassword shouldn't be null: " + passwordDict.get("passwordDictContentEntries"));
}
} catch (Exception e) {
throw Throwables.propagate(e);
}
try {
final AtomicReference<String> encryptedPassword = Atomics.newReference();
boolean passwordRetrieved = Predicates2.retry(new Predicate<Instance>() {
public boolean apply(Instance instance) {
String serialPortContents = instanceApi.getSerialPortOutput(instance.name(), 4).contents();
List<String> contentEntries = Splitter.on('\n').splitToList(serialPortContents);
Optional<String> retrievedPassword = tryFind(
filter(transform(contentEntries, deserializeSerialOutput(json)), notNull()), HasEncryptedPassword)
.transform(ExtractEncryptedPassword);
if (retrievedPassword.isPresent()) {
encryptedPassword.set(retrievedPassword.get());
}
return retrievedPassword.isPresent();
}
// Notice that timeoutDuration should be less than EXPIRE_DURATION
}, 10 * 60, 30, TimeUnit.SECONDS).apply(instance.get());
if (passwordRetrieved) {
return decryptPassword(encryptedPassword.get(), keys);
} else {
throw new IllegalStateException("Did not find the encrypted password in the serial port output");
}
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private static Function<String, Map<String, Object>> deserializeSerialOutput(final Json json) {
return new Function<String, Map<String, Object>>() {
@Override
public Map<String, Object> apply(String input) {
try {
return json.fromJson(input, new TypeLiteral<Map<String, Object>>() {
}.getType());
} catch (Exception ex) {
return null;
}
}
};
}
private static final Predicate<Map<String, Object>> HasEncryptedPassword = new Predicate<Map<String, Object>>() {
@Override
public boolean apply(Map<String, Object> input) {
return input.containsKey("encryptedPassword");
}
};
private static final Function<Map<String, Object>, String> ExtractEncryptedPassword = new Function<Map<String, Object>, String>() {
@Override
public String apply(Map<String, Object> input) {
return (String) input.get("encryptedPassword");
}
};
/**
* Decrypts the given password - the encrypted text is base64-encoded.
* As per the GCE docs, assumes it was encrypted with algorithm "RSA/NONE/OAEPPadding", and UTF-8.

View File

@ -208,34 +208,32 @@ public final class CreateNodesWithGroupEncodedIntoNameThenAddToSet extends
}
int[] inboundPorts = templateOptions.getInboundPorts();
if ((inboundPorts == null) || inboundPorts.length == 0){
return;
}
if (inboundPorts != null && inboundPorts.length > 0) {
List<String> ports = simplifyPorts(inboundPorts);
String name = naming.name(ports);
Firewall firewall = firewallApi.get(name);
AtomicReference<Operation> operation = null;
List<String> ports = simplifyPorts(inboundPorts);
String name = naming.name(ports);
Firewall firewall = firewallApi.get(name);
AtomicReference<Operation> operation = null;
String interiorRange = subnet.isPresent() ? subnet.get().ipCidrRange() : DEFAULT_INTERNAL_NETWORK_RANGE;
if (firewall == null) {
List<Rule> rules = ImmutableList.of(Rule.create("tcp", ports), Rule.create("udp", ports));
FirewallOptions firewallOptions = new FirewallOptions().name(name).network(network.selfLink())
String interiorRange = subnet.isPresent() ? subnet.get().ipCidrRange() : DEFAULT_INTERNAL_NETWORK_RANGE;
if (firewall == null) {
List<Rule> rules = ImmutableList.of(Rule.create("tcp", ports), Rule.create("udp", ports));
FirewallOptions firewallOptions = new FirewallOptions().name(name).network(network.selfLink())
.allowedRules(rules).sourceTags(templateOptions.getTags())
.sourceRanges(of(interiorRange, EXTERIOR_RANGE))
.targetTags(ImmutableList.of(name));
.sourceRanges(of(interiorRange, EXTERIOR_RANGE)).targetTags(ImmutableList.of(name));
operation = Atomics.newReference(firewallApi
.createInNetwork(firewallOptions.name(), network.selfLink(), firewallOptions));
operation = Atomics.newReference(firewallApi.createInNetwork(firewallOptions.name(), network.selfLink(),
firewallOptions));
operationDone.apply(operation);
checkState(operation.get().httpErrorStatusCode() == null, "Could not insert firewall, operation failed %s",
operation);
operationDone.apply(operation);
checkState(operation.get().httpErrorStatusCode() == null, "Could not insert firewall, operation failed %s",
operation);
}
// Add tags for firewalls
tags.add(name);
tags.add(name); // Add tags for the inbound ports firewall
}
templateOptions.tags(tags);
}

View File

@ -82,7 +82,7 @@ public abstract class Image {
public abstract Status status();
public abstract Long archiveSizeBytes();
@Nullable public abstract Long archiveSizeBytes();
public abstract Long diskSizeGb();

View File

@ -20,6 +20,8 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import com.google.gson.Gson;
import org.jclouds.crypto.Crypto;
import org.jclouds.encryption.bouncycastle.BouncyCastleCrypto;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
@ -29,6 +31,7 @@ import org.jclouds.googlecomputeengine.domain.Metadata;
import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.features.InstanceApi;
import org.jclouds.googlecomputeengine.parse.ParseInstanceTest;
import org.jclouds.json.internal.GsonWrapper;
import org.testng.annotations.Test;
import javax.crypto.Cipher;
@ -93,7 +96,7 @@ public class ResetWindowsPasswordTest {
replay(api, instanceApi, operation, serialPortOutput, crypto, keyPairGenerator);
ResetWindowsPassword generator = new ResetWindowsPassword(api, crypto, operationDone);
ResetWindowsPassword generator = new ResetWindowsPassword(api, crypto, operationDone, new GsonWrapper(new Gson()));
String result = generator.apply(ImmutableMap.of("instance", new AtomicReference<Instance>(instance), "zone", zone, "email", "test@google.com", "userName", "test"));
verify(api, instanceApi, operation, serialPortOutput);

View File

@ -53,7 +53,7 @@ public class AddressApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertEquals(addresses.next().size(), 1);
}
@Test(groups = "live", dependsOnMethods = "testListAddress")
@Test(groups = "live", dependsOnMethods = "testListAddress", alwaysRun = true)
public void testDeleteAddress() {
assertOperationDoneSuccessfully(api().delete(ADDRESS_NAME));
}

View File

@ -103,7 +103,7 @@ public class BackendServiceApiLiveTest extends BaseGoogleComputeEngineApiLiveTes
}
*/
@Test(groups = "live", dependsOnMethods = "testListBackendService")
@Test(groups = "live", dependsOnMethods = "testListBackendService", alwaysRun = true)
public void testDeleteBackendService() {
assertOperationDoneSuccessfully(api().delete(BACKEND_SERVICE_NAME));

View File

@ -64,7 +64,7 @@ public class DiskApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertDiskEquals(disksAsList.get(0));
}
@Test(groups = "live", dependsOnMethods = "testListDisk")
@Test(groups = "live", dependsOnMethods = "testListDisk", alwaysRun = true)
public void testDeleteDisk() {
assertOperationDoneSuccessfully(api().delete(DISK_NAME));
}

View File

@ -107,7 +107,7 @@ public class FirewallApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertEquals(firewalls.next().size(), 1);
}
@Test(groups = "live", dependsOnMethods = "testListFirewall")
@Test(groups = "live", dependsOnMethods = "testListFirewall", alwaysRun = true)
public void testDeleteFirewall() {
assertOperationDoneSuccessfully(api().delete(FIREWALL_NAME));
assertOperationDoneSuccessfully(api.networks().delete(FIREWALL_NETWORK_NAME));

View File

@ -73,7 +73,7 @@ public class ForwardingRuleApiLiveTest extends BaseGoogleComputeEngineApiLiveTes
address = addressApi().get(ADDRESS_NAME);
}
@AfterClass
@AfterClass(alwaysRun = true)
public void tearDown() {
assertOperationDoneSuccessfully(targetPoolApi().delete(TARGETPOOL_NAME));
assertOperationDoneSuccessfully(targetPoolApi().delete(TARGETPOOL_NAME_NEW));

View File

@ -104,7 +104,7 @@ public class GlobalForwardingRuleApiLiveTest extends BaseGoogleComputeEngineApiL
assertEquals(forwardingRules.size(), 1);
}
@Test(groups = "live", dependsOnMethods = "testListGlobalForwardingRule")
@Test(groups = "live", dependsOnMethods = "testListGlobalForwardingRule", alwaysRun = true)
public void testDeleteGlobalForwardingRule() {
assertOperationDoneSuccessfully(api().delete(GLOBAL_FORWARDING_RULE_NAME));

View File

@ -354,7 +354,7 @@ public class InstanceApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertEquals(resultTags, expectedTags);
}
@AfterClass(groups = { "integration", "live" })
@AfterClass(groups = { "integration", "live" }, alwaysRun = true)
protected void tearDownContext() {
try {
waitOperationDone(api().delete(INSTANCE_NAME));

View File

@ -119,7 +119,7 @@ public class InstanceApiWindowsLiveTest extends BaseGoogleComputeEngineApiLiveTe
assertFalse(Strings.isNullOrEmpty(result), "Password shouldn't be empty");
}
@AfterClass(groups = { "integration", "live" })
@AfterClass(groups = { "integration", "live" }, alwaysRun = true)
protected void tearDownContext() {
try {
waitOperationDone(api().delete(INSTANCE_NAME));

View File

@ -61,7 +61,7 @@ public class NetworkApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertNetworkEquals(networksAsList.get(0));
}
@Test(groups = "live", dependsOnMethods = "testListNetwork")
@Test(groups = "live", dependsOnMethods = "testListNetwork", alwaysRun = true)
public void testDeleteNetwork() {
assertOperationDoneSuccessfully(api().delete(NETWORK_NAME));
}

View File

@ -64,7 +64,7 @@ public class ProjectApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertNotNull(project.commonInstanceMetadata().fingerprint());
}
@Test(groups = "live", dependsOnMethods = "addItemToMetadata")
@Test(groups = "live", dependsOnMethods = "addItemToMetadata", alwaysRun = true)
public void testDeleteItemFromMetadata() {
Metadata metadata = project.commonInstanceMetadata().remove(METADATA_ITEM_KEY);
assertOperationDoneSuccessfully(api.project().setCommonInstanceMetadata(metadata));

View File

@ -75,7 +75,7 @@ public class RouteApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertRouteEquals(routesAsList.get(0));
}
@Test(groups = "live", dependsOnMethods = "testListRoute")
@Test(groups = "live", dependsOnMethods = "testListRoute", alwaysRun = true)
public void testDeleteRoute() {
assertOperationDoneSuccessfully(api().delete(ROUTE_NAME));
assertOperationDoneSuccessfully(api.networks().delete(ROUTE_NETWORK_NAME));

View File

@ -71,7 +71,7 @@ public class SnapshotApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertSnapshotEquals(snapshotsAsList.get(0));
}
@Test(groups = "live", dependsOnMethods = "testListSnapshot")
@Test(groups = "live", dependsOnMethods = "testListSnapshot", alwaysRun = true)
public void testDeleteDisk() {
assertOperationDoneSuccessfully(diskApi().delete(DISK_NAME));
assertOperationDoneSuccessfully(api().delete(SNAPSHOT_NAME));

View File

@ -258,7 +258,7 @@ public class TargetPoolApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertOperationDoneSuccessfully(api().delete(BACKUP_TARGETPOOL_NAME));
}
@AfterClass(groups = { "integration", "live" })
@AfterClass(groups = { "integration", "live" }, alwaysRun = true)
public void testCleanup(){
InstanceApi instanceApi = api.instancesInZone(DEFAULT_ZONE_NAME);
HttpHealthCheckApi httpHealthCheckApi = api.httpHeathChecks();

View File

@ -13,7 +13,7 @@
</encoder>
</appender>
<appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
<file>target/jclouds-compute.log</file>
<file>target/test-data/jclouds-compute.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>