diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java index 145871d9a4..25c5157108 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java @@ -131,8 +131,8 @@ public class NovaComputeServiceAdapter implements logger.trace("<< server(%s)", server.getId()); ServerInZone serverInZone = new ServerInZone(server, zoneId); - if (!privateKey.isPresent()) - credentialsBuilder.password(lightweightServer.getAdminPass()); + if (!privateKey.isPresent() && lightweightServer.getAdminPass().isPresent()) + credentialsBuilder.password(lightweightServer.getAdminPass().get()); return new NodeAndInitialCredentials(serverInZone, serverInZone.slashEncode(), credentialsBuilder .build()); } diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java index 44acfaca7c..20be1148f7 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/domain/ServerCreated.java @@ -18,8 +18,6 @@ */ package org.jclouds.openstack.nova.v2_0.domain; -import static com.google.common.base.Preconditions.checkNotNull; - import java.beans.ConstructorProperties; import java.util.Set; @@ -29,6 +27,7 @@ import org.jclouds.openstack.v2_0.domain.Resource; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import com.google.common.base.Optional; /** * Server Resource with administrative password returned by ServerApi#CreateServer calls @@ -40,21 +39,21 @@ import com.google.common.base.Objects.ToStringHelper; */ public class ServerCreated extends Resource { - public static Builder builder() { - return new ConcreteBuilder(); + public static Builder builder() { + return new Builder(); } - public Builder toBuilder() { - return new ConcreteBuilder().fromServerCreated(this); + public Builder toBuilder() { + return builder().fromServerCreated(this); } - public static abstract class Builder> extends Resource.Builder { + public final static class Builder extends Resource.Builder { protected String adminPass; /** * @see ServerCreated#getAdminPass() */ - public T adminPass(String adminPass) { + public Builder adminPass(String adminPass) { this.adminPass = adminPass; return self(); } @@ -63,33 +62,30 @@ public class ServerCreated extends Resource { return new ServerCreated(id, name, links, adminPass); } - public T fromServerCreated(ServerCreated in) { - return super.fromResource(in) - .adminPass(in.getAdminPass()); + public Builder fromServerCreated(ServerCreated in) { + return super.fromResource(in).adminPass(in.getAdminPass().orNull()); } - } - private static class ConcreteBuilder extends Builder { @Override - protected ConcreteBuilder self() { + protected Builder self() { return this; } } - private final String adminPass; + private final Optional adminPass; @ConstructorProperties({ "id", "name", "links", "adminPass" }) - protected ServerCreated(String id, @Nullable String name, Set links, String adminPass) { + protected ServerCreated(String id, @Nullable String name, Set links, @Nullable String adminPass) { super(id, name, links); - this.adminPass = checkNotNull(adminPass, "adminPass"); + this.adminPass = Optional.fromNullable(adminPass); } /** - * @return the administrative password for this server. Note: this is not available in Server responses. + * present unless the nova install was configured with the option {@code enable_instance_password=false} */ - public String getAdminPass() { + public Optional getAdminPass() { return this.adminPass; } @@ -105,10 +101,9 @@ public class ServerCreated extends Resource { ServerCreated that = ServerCreated.class.cast(obj); return super.equals(that) && Objects.equal(this.adminPass, that.adminPass); } - + + @Override protected ToStringHelper string() { - return super.string() - .add("adminPass", adminPass); + return super.string().add("adminPass", adminPass.orNull()); } - } diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java index d103060bfb..3c5161e570 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java @@ -20,6 +20,7 @@ package org.jclouds.openstack.nova.v2_0.compute; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; import java.util.Map; import java.util.Properties; @@ -51,6 +52,15 @@ import com.google.inject.TypeLiteral; */ @Test(groups = "unit", testName = "NovaComputeServiceAdapterExpectTest") public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceContextExpectTest { + HttpRequest serverDetail = HttpRequest + .builder() + .method("GET") + .endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/71752") + .addHeader("Accept", "application/json") + .addHeader("X-Auth-Token", authToken).build(); + + HttpResponse serverDetailResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/server_details.json")).build(); public void testCreateNodeWithGroupEncodedIntoNameWhenSecurityGroupsArePresent() throws Exception { @@ -67,16 +77,6 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted") .payload(payloadFromResourceWithContentType("/new_server.json","application/json; charset=UTF-8")).build(); - HttpRequest serverDetail = HttpRequest - .builder() - .method("GET") - .endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/71752") - .addHeader("Accept", "application/json") - .addHeader("X-Auth-Token", authToken).build(); - - HttpResponse serverDetailResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResource("/server_details.json")).build(); - Map requestResponseMap = ImmutableMap. builder() .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess) .put(extensionsOfNovaRequest, extensionsOfNovaResponse) @@ -117,17 +117,7 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted") - .payload(payloadFromResourceWithContentType("/new_server.json","application/json; charset=UTF-8")).build(); - - HttpRequest serverDetail = HttpRequest - .builder() - .method("GET") - .endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers/71752") - .addHeader("Accept", "application/json") - .addHeader("X-Auth-Token", authToken).build(); - - HttpResponse serverDetailResponse = HttpResponse.builder().statusCode(200) - .payload(payloadFromResource("/server_details.json")).build(); + .payload(payloadFromResourceWithContentType("/new_server_no_adminpass.json","application/json; charset=UTF-8")).build(); Map requestResponseMap = ImmutableMap. builder() .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess) @@ -156,6 +146,47 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC assertNotNull(server); assertEquals(server.getCredentials(), LoginCredentials.builder().privateKey("privateKey").build()); } + + + /** + * When enable_instance_password is false, then no admin pass is generated. + * However in this case if you don't specify the name of the SSH keypair to + * inject, then you simply cannot log in to the server. + */ + public void testNoKeyPairOrAdminPass() throws Exception { + + HttpRequest createServer = HttpRequest + .builder() + .method("POST") + .endpoint("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/servers") + .addHeader("Accept", "application/json") + .addHeader("X-Auth-Token", authToken) + .payload(payloadFromStringWithContentType( + "{\"server\":{\"name\":\"test-e92\",\"imageRef\":\"1241\",\"flavorRef\":\"100\"}}","application/json")) + .build(); + + HttpResponse createServerResponse = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted") + .payload(payloadFromResourceWithContentType("/new_server_no_adminpass.json","application/json; charset=UTF-8")).build(); + + Map requestResponseMap = ImmutableMap. builder() + .put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess) + .put(extensionsOfNovaRequest, extensionsOfNovaResponse) + .put(listDetail, listDetailResponse) + .put(listFlavorsDetail, listFlavorsDetailResponse) + .put(createServer, createServerResponse) + .put(serverDetail, serverDetailResponse).build(); + + Injector forSecurityGroups = requestsSendResponses(requestResponseMap); + + Template template = forSecurityGroups.getInstance(TemplateBuilder.class).build(); + + NovaComputeServiceAdapter adapter = forSecurityGroups.getInstance(NovaComputeServiceAdapter.class); + + NodeAndInitialCredentials server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92", + template); + assertNotNull(server); + assertNull(server.getCredentials()); + } @Override public Injector apply(ComputeServiceContext input) { diff --git a/apis/openstack-nova/src/test/resources/new_server_no_adminpass.json b/apis/openstack-nova/src/test/resources/new_server_no_adminpass.json new file mode 100644 index 0000000000..a89e48f44d --- /dev/null +++ b/apis/openstack-nova/src/test/resources/new_server_no_adminpass.json @@ -0,0 +1,40 @@ +{ + "server": { + "status": "BUILD(scheduling)", + "updated": "2012-03-19T06:21:13Z", + "hostId": "", + "user_id": "54297837463082", + "name": "test-e92", + "links": [{ + "href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/37936628937291/servers/71752", + "rel": "self" + }, { + "href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/servers/71752", + "rel": "bookmark" + }], + "addresses": {}, + "tenant_id": "37936628937291", + "image": { + "id": "1241", + "links": [{ + "href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/images/1241", + "rel": "bookmark" + }] + }, + "created": "2012-03-19T06:21:13Z", + "uuid": "47491020-6a78-4f63-9475-23195ac4515c", + "accessIPv4": "", + "accessIPv6": "", + "key_name": null, + "flavor": { + "id": "100", + "links": [{ + "href": "https://az-1.region-a.geo-1.compute.hpcloudsvc.com/37936628937291/flavors/100", + "rel": "bookmark" + }] + }, + "config_drive": "", + "id": 71752, + "metadata": {} + } +} \ No newline at end of file