diff --git a/adapter/common/src/main/java/com/vmware/admiral/adapter/pks/PKSConstants.java b/adapter/common/src/main/java/com/vmware/admiral/adapter/pks/PKSConstants.java new file mode 100644 index 000000000..df55bb5bb --- /dev/null +++ b/adapter/common/src/main/java/com/vmware/admiral/adapter/pks/PKSConstants.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 VMware, Inc. All Rights Reserved. + * + * This product is licensed to you under the Apache License, Version 2.0 (the "License"). + * You may not use this product except in compliance with the License. + * + * This product may include a number of subcomponents with separate copyright notices + * and license terms. Your use of these subcomponents is subject to the terms and + * conditions of the subcomponent's license, as noted in the LICENSE file. + */ + +package com.vmware.admiral.adapter.pks; + +/** + * Set of constants related with PKS + */ +public interface PKSConstants { + + String CLUSTER_NAME_PROP_NAME = "__clusterName"; + String VALIDATE_CONNECTION = "validate_connection"; + String CREDENTIALS_LINK = "credentials"; + +} diff --git a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSOperationType.java b/adapter/common/src/main/java/com/vmware/admiral/adapter/pks/PKSOperationType.java similarity index 94% rename from adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSOperationType.java rename to adapter/common/src/main/java/com/vmware/admiral/adapter/pks/PKSOperationType.java index 3c0b6514d..dc8a798cc 100644 --- a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSOperationType.java +++ b/adapter/common/src/main/java/com/vmware/admiral/adapter/pks/PKSOperationType.java @@ -19,7 +19,8 @@ public enum PKSOperationType { GET_CLUSTER("PKS.GetCluster"), CREATE_USER("PKS.CreateUser"), CREATE_CLUSTER("PKS.CreateCluster"), - DELETE_CLUSTER("PKS.DeleteCluster"); + DELETE_CLUSTER("PKS.DeleteCluster"), + LIST_PLANS("PKS.ListPlans"); public final String id; diff --git a/adapter/kubernetes/src/test/java/com/vmware/admiral/adapter/pks/PKSOperationTypeTest.java b/adapter/common/src/test/java/com/vmware/admiral/adapter/pks/PKSOperationTypeTest.java similarity index 100% rename from adapter/kubernetes/src/test/java/com/vmware/admiral/adapter/pks/PKSOperationTypeTest.java rename to adapter/common/src/test/java/com/vmware/admiral/adapter/pks/PKSOperationTypeTest.java diff --git a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSContext.java b/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSContext.java index 1d3c3c9a9..b922cd244 100644 --- a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSContext.java +++ b/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/PKSContext.java @@ -24,16 +24,18 @@ public class PKSContext { public URI pksAPIUri; public String accessToken; public String refreshToken; - public long expireMillisTime; + public long expireMillisTime = 0; public static PKSContext create(PKSEndpointService.Endpoint endpoint, UAATokenResponse uaaTokenResponse) { PKSContext pksContext = new PKSContext(); - pksContext.accessToken = uaaTokenResponse.accessToken; - pksContext.refreshToken = uaaTokenResponse.refreshToken; pksContext.pksUAAUri = URI.create(endpoint.uaaEndpoint); pksContext.pksAPIUri = URI.create(endpoint.apiEndpoint); - pksContext.expireMillisTime = calculateExpireTime(uaaTokenResponse.expiresIn); + if (uaaTokenResponse != null) { + pksContext.accessToken = uaaTokenResponse.accessToken; + pksContext.refreshToken = uaaTokenResponse.refreshToken; + pksContext.expireMillisTime = calculateExpireTime(uaaTokenResponse.expiresIn); + } return pksContext; } diff --git a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSAdapterService.java b/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSAdapterService.java index 9145ebdc3..b7c31caff 100644 --- a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSAdapterService.java +++ b/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSAdapterService.java @@ -11,9 +11,11 @@ package com.vmware.admiral.adapter.pks.service; -import java.net.URI; +import static com.vmware.admiral.adapter.pks.PKSConstants.CLUSTER_NAME_PROP_NAME; +import static com.vmware.admiral.adapter.pks.PKSConstants.VALIDATE_CONNECTION; + +import java.util.HashMap; import java.util.LinkedList; -import java.util.List; import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; @@ -25,6 +27,7 @@ import com.google.common.cache.CacheBuilder; import com.vmware.admiral.adapter.common.AdapterRequest; +import com.vmware.admiral.adapter.pks.PKSConstants; import com.vmware.admiral.adapter.pks.PKSContext; import com.vmware.admiral.adapter.pks.PKSException; import com.vmware.admiral.adapter.pks.PKSOperationType; @@ -36,7 +39,7 @@ import com.vmware.admiral.common.util.DeferredUtils; import com.vmware.admiral.common.util.PropertyUtils; import com.vmware.admiral.common.util.ServerX509TrustManager; -import com.vmware.admiral.compute.pks.PKSEndpointService; +import com.vmware.admiral.compute.pks.PKSEndpointService.Endpoint; import com.vmware.photon.controller.model.resources.ComputeService.ComputeState; import com.vmware.photon.controller.model.security.util.AuthCredentialsType; import com.vmware.photon.controller.model.security.util.EncryptionUtils; @@ -50,8 +53,6 @@ public class PKSAdapterService extends StatelessService { public static final String SELF_LINK = ManagementUriParts.ADAPTER_PKS; - public static final String CLUSTER_NAME_PROP_NAME = "__clusterName"; - private static final long MAINTENANCE_INTERVAL_MICROS = Long.getLong( "dcp.management.docker.adapter.periodic.maintenance.period.micros", TimeUnit.SECONDS.toMicros(10)); @@ -63,10 +64,10 @@ public class PKSAdapterService extends StatelessService { private static final long REMOVE_TOKEN_BEFORE_EXPIRE_MILLIS = 2 * MAINTENANCE_INTERVAL_MICROS; - public static class RequestContext extends AdapterRequest { + private static class RequestContext { public Operation operation; - public List tenantLinks; - public PKSEndpointService.Endpoint endpoint; + public AdapterRequest request; + public Endpoint endpoint; public ComputeState computeState; } @@ -93,11 +94,11 @@ public void handleStop(Operation op) { @Override public void handlePatch(Operation op) { - RequestContext ctx = op.getBody(RequestContext.class); - ctx.validate(); + RequestContext ctx = new RequestContext(); + ctx.request = getRequest(op); ctx.operation = op; - getEndpoint(ctx.resourceReference) + getEndpoint(ctx) .thenAccept(e -> ctx.endpoint = e) .thenCompose(aVoid -> processOperationWithRetry(ctx)) .exceptionally(t -> { @@ -137,7 +138,7 @@ public void handlePeriodicMaintenance(Operation op) { } private DeferredResult processOperationWithRetry(RequestContext ctx) { - PKSOperationType ot = PKSOperationType.instanceById(ctx.operationTypeId); + PKSOperationType ot = PKSOperationType.instanceById(ctx.request.operationTypeId); logInfo("Received [%s] for endpoint [%s]", ot.getDisplayName(), ctx.endpoint.name); DeferredResult result = new DeferredResult<>(); @@ -164,26 +165,8 @@ private Function, DeferredResult> processOperationTask return (task) -> { DeferredResult result = new DeferredResult<>(); try { - switch (operationType) { - case LIST_CLUSTERS: - result = pksListClusters(ctx); - break; - case GET_CLUSTER: - result = pksGetCluster(ctx); - break; - case CREATE_USER: - result = pksCreateUser(ctx); - break; - /* - case CREATE_CLUSTER: - //TODO implementation - break; - case DELETE_CLUSTER: - //TODO implementation - break; - */ - default: - result.fail(new IllegalArgumentException("unsupported operation")); + result = initDeferredOperation(operationType, ctx); + if (result.toCompletionStage().toCompletableFuture().isCompletedExceptionally()) { return result; } @@ -193,12 +176,16 @@ private Function, DeferredResult> processOperationTask if (t instanceof PKSException) { PKSException p = (PKSException) t; if (p.getErrorCode() == Operation.STATUS_CODE_UNAUTHORIZED) { - logInfo("Operation for %s returned code 401, invalidate token and retry", + logInfo("Operation for %s returns code 401, invalidate token and retry", ctx.endpoint.documentSelfLink); - pksContextCache.invalidate(ctx.endpoint.documentSelfLink); + if (ctx.endpoint.documentSelfLink != null) { + pksContextCache.invalidate(ctx.endpoint.documentSelfLink); + } } else { task.preventRetries(); } + } else { + task.preventRetries(); } throw DeferredUtils.wrap(t); }); @@ -210,9 +197,39 @@ private Function, DeferredResult> processOperationTask }; } + private DeferredResult initDeferredOperation(PKSOperationType operationType, + RequestContext ctx) { + DeferredResult result = new DeferredResult<>(); + switch (operationType) { + case LIST_CLUSTERS: + result = pksListClusters(ctx); + break; + case GET_CLUSTER: + result = pksGetCluster(ctx); + break; + case CREATE_USER: + result = pksCreateUser(ctx); + break; + /* + case CREATE_CLUSTER: + //TODO implementation + break; + case DELETE_CLUSTER: + //TODO implementation + break; + */ + case LIST_PLANS: + result = pksListPlans(ctx); + break; + default: + result.fail(new IllegalArgumentException("unsupported operation")); + } + return result; + } + private DeferredResult pksCreateUser(RequestContext ctx) { String cluster = PropertyUtils - .getPropertyString(ctx.customProperties, CLUSTER_NAME_PROP_NAME) + .getPropertyString(ctx.request.customProperties, CLUSTER_NAME_PROP_NAME) .orElse(null); if (cluster == null) { @@ -245,18 +262,32 @@ private DeferredResult pksGetCluster(RequestContext ctx) { .thenAccept(pksCluster -> ctx.operation.setBodyNoCloning(pksCluster).complete()); } - private DeferredResult getEndpoint(URI uri) { - Operation op = Operation.createGet(this, uri.getPath()); - return sendWithDeferredResult(op, PKSEndpointService.Endpoint.class) + private DeferredResult pksListPlans(RequestContext ctx) { + return getPKSContext(ctx.endpoint) + .thenCompose(pksContext -> getClient().getPlans(pksContext)) + .thenAccept(pksPlans -> ctx.operation.setBodyNoCloning(pksPlans).complete()); + } + + private DeferredResult getEndpoint(RequestContext ctx) { + if (ctx.request.customProperties.containsKey(VALIDATE_CONNECTION)) { + return buildFakeEndpoint(ctx); + } + + String path = ctx.request.resourceReference.getPath(); + Operation op = Operation.createGet(this, path); + return sendWithDeferredResult(op, Endpoint.class) .exceptionally(ex -> { throw DeferredUtils.logErrorAndThrow(ex, e -> String.format("Unable to get PKS endpoint state %s, reason: %s", - uri.getPath(), e.getMessage()), + path, e.getMessage()), getClass()); }); } private DeferredResult getCredentials(String selfLink) { + if (selfLink == null || selfLink.isEmpty()) { + return DeferredResult.completed(null); + } Operation op = Operation.createGet(this, selfLink); return sendWithDeferredResult(op, AuthCredentialsServiceState.class) .exceptionally(ex -> { @@ -267,7 +298,7 @@ private DeferredResult getCredentials(String selfLi }); } - private DeferredResult getPKSContext(PKSEndpointService.Endpoint endpoint) { + private DeferredResult getPKSContext(Endpoint endpoint) { DeferredResult result = new DeferredResult<>(); new RetriableTaskBuilder("get-token-from-" + endpoint.uaaEndpoint) .withMaximumRetries(1) @@ -290,21 +321,26 @@ private DeferredResult getPKSContext(PKSEndpointService.Endpoint end } private Function, DeferredResult> retriableLoginTask( - PKSEndpointService.Endpoint endpoint) { + Endpoint endpoint) { return (task) -> { DeferredResult result = new DeferredResult<>(); try { - result.complete(pksContextCache.get(endpoint.documentSelfLink, - () -> createNewPKSContext(endpoint))); - } catch (ExecutionException e) { + if (endpoint.customProperties != null + && endpoint.customProperties.get(VALIDATE_CONNECTION) != null) { + result.complete(createNewPKSContext(endpoint)); + } else { + result.complete(pksContextCache.get(endpoint.documentSelfLink, + () -> createNewPKSContext(endpoint))); + } + } catch (Exception e) { result.fail(e); } return result; }; } - private PKSContext createNewPKSContext(PKSEndpointService.Endpoint endpoint) + private PKSContext createNewPKSContext(Endpoint endpoint) throws ExecutionException, InterruptedException { return getCredentials(endpoint.authCredentialsLink) .thenCompose(authCredentials -> login(endpoint, authCredentials)) @@ -316,14 +352,19 @@ private PKSContext createNewPKSContext(PKSEndpointService.Endpoint endpoint) .get(); } - private DeferredResult login(PKSEndpointService.Endpoint endpoint, + /** + * Login in PKS and returns PKS context instance with token. + */ + private DeferredResult login(Endpoint endpoint, AuthCredentialsServiceState authCredentials) { + if (authCredentials == null) { + return DeferredResult.completed(PKSContext.create(endpoint, null)); + } AuthCredentialsType authCredentialsType = AuthCredentialsType.valueOf(authCredentials.type); if (AuthCredentialsType.Password == authCredentialsType) { String username = authCredentials.userEmail; String password = EncryptionUtils.decrypt(authCredentials.privateKey); - //TODO run this in separate thread return getClient() .login(endpoint.uaaEndpoint, username, password) .thenApply(uaaTokenResponse -> PKSContext.create(endpoint, uaaTokenResponse)); @@ -333,6 +374,35 @@ private DeferredResult login(PKSEndpointService.Endpoint endpoint, + " is not supported"); } + private AdapterRequest getRequest(Operation op) { + AdapterRequest request = op.getBody(AdapterRequest.class); + request.validate(); + if (request.customProperties == null) { + request.customProperties = new HashMap<>(2); + } + return request; + } + + /** + * Construct fake {@link Endpoint} state used only to validate connection. + */ + private DeferredResult buildFakeEndpoint(RequestContext ctx) { + Endpoint e = new Endpoint(); + e.customProperties = new HashMap<>(2); + e.customProperties.put(VALIDATE_CONNECTION, "true"); + e.name = "test-connection"; + e.uaaEndpoint = ctx.request.customProperties.get(Endpoint.FIELD_NAME_UAA_ENDPOINT); + e.apiEndpoint = ctx.request.customProperties.get(Endpoint.FIELD_NAME_API_ENDPOINT); + e.authCredentialsLink = ctx.request.customProperties.get(PKSConstants.CREDENTIALS_LINK); + ctx.endpoint = e; + DeferredResult result = new DeferredResult<>(); + result.complete(e); + return result; + } + + /** + * Expire cached tokens. + */ private void expireCachedTokens() { ConcurrentMap map = pksContextCache.asMap(); long currentTime = System.currentTimeMillis(); diff --git a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSClusterConfigService.java b/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSClusterConfigService.java index d169e4e70..a1118e64f 100644 --- a/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSClusterConfigService.java +++ b/adapter/kubernetes/src/main/java/com/vmware/admiral/adapter/pks/service/PKSClusterConfigService.java @@ -11,7 +11,7 @@ package com.vmware.admiral.adapter.pks.service; -import static com.vmware.admiral.adapter.pks.service.PKSAdapterService.CLUSTER_NAME_PROP_NAME; +import static com.vmware.admiral.adapter.pks.PKSConstants.CLUSTER_NAME_PROP_NAME; import static com.vmware.admiral.common.util.OperationUtil.PROJECT_ADMIRAL_HEADER; import static com.vmware.admiral.compute.ComputeConstants.HOST_AUTH_CREDENTIALS_PROP_NAME; import static com.vmware.admiral.compute.ContainerHostService.CONTAINER_HOST_TYPE_PROP_NAME; @@ -115,7 +115,7 @@ public void handlePost(Operation op) { private void handleAddRequest(Operation op, AddClusterRequest clusterRequest) { AdapterRequest adapterRequest = new AdapterRequest(); - adapterRequest.operationTypeId = PKSOperationType.CREATE_USER.toString(); + adapterRequest.operationTypeId = PKSOperationType.CREATE_USER.id; adapterRequest.serviceTaskCallback = ServiceTaskCallback.createEmpty(); adapterRequest.resourceReference = UriUtils.buildUri(getHost(), clusterRequest.endpointLink); @@ -126,7 +126,7 @@ private void handleAddRequest(Operation op, AddClusterRequest clusterRequest) { .setBodyNoCloning(adapterRequest) .setCompletion((o, ex) -> { if (ex != null) { - logSevere("Adapter request for listing PKS clusters failed. Error: %s", + logSevere("Adapter request for add PKS cluster failed. Error: %s", Utils.toString(ex)); op.fail(ex); } else { @@ -219,4 +219,5 @@ private void setProjectLinkAsTenantLink(Operation op, AddClusterRequest request) request.tenantLinks.add(projectLink); } } + } diff --git a/adapter/kubernetes/src/test/java/com/vmware/admiral/adapter/pks/PKSContextTest.java b/adapter/kubernetes/src/test/java/com/vmware/admiral/adapter/pks/PKSContextTest.java index 67288a66a..d065957dc 100644 --- a/adapter/kubernetes/src/test/java/com/vmware/admiral/adapter/pks/PKSContextTest.java +++ b/adapter/kubernetes/src/test/java/com/vmware/admiral/adapter/pks/PKSContextTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.Before; @@ -52,6 +53,15 @@ public void test() { assertEquals(uaaTokenResponse.accessToken, pksContext.accessToken); assertEquals(uaaTokenResponse.refreshToken, pksContext.refreshToken); assertTrue(pksContext.expireMillisTime > System.currentTimeMillis()); + + pksContext = PKSContext.create(endpoint, null); + assertNotNull(pksContext.pksUAAUri); + assertEquals(endpoint.uaaEndpoint, pksContext.pksUAAUri.toString()); + assertNotNull(pksContext.pksAPIUri); + assertEquals(endpoint.apiEndpoint, pksContext.pksAPIUri.toString()); + assertNull(pksContext.accessToken); + assertNull(pksContext.refreshToken); + assertEquals(0, pksContext.expireMillisTime); } @Test diff --git a/common/src/main/java/com/vmware/admiral/common/ManagementUriParts.java b/common/src/main/java/com/vmware/admiral/common/ManagementUriParts.java index 69f69a3cf..9d202167d 100644 --- a/common/src/main/java/com/vmware/admiral/common/ManagementUriParts.java +++ b/common/src/main/java/com/vmware/admiral/common/ManagementUriParts.java @@ -145,6 +145,7 @@ public interface ManagementUriParts { String PKS_NAMESPACE = "/pks"; String PKS_ENDPOINTS = RESOURCES + PKS_NAMESPACE + "/endpoints"; + String PKS_CREATE_ENDPOINT = RESOURCES + PKS_NAMESPACE + "/create-endpoint"; String PKS_CLUSTERS = RESOURCES + PKS_NAMESPACE + "/clusters"; String PKS_CLUSTERS_CONFIG = RESOURCES + PKS_NAMESPACE + "/clusters-config"; diff --git a/common/src/main/java/com/vmware/admiral/common/serialization/ReleaseConstants.java b/common/src/main/java/com/vmware/admiral/common/serialization/ReleaseConstants.java index 58ed46c80..ac5650756 100644 --- a/common/src/main/java/com/vmware/admiral/common/serialization/ReleaseConstants.java +++ b/common/src/main/java/com/vmware/admiral/common/serialization/ReleaseConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 VMware, Inc. All Rights Reserved. + * Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. @@ -64,6 +64,13 @@ private ReleaseConstants() { public static final String API_VERSION_1_2_2 = "1.2.2"; public static final String VERSION_HEADER_1_2_2 = VERSION_PREFIX + API_VERSION_1_2_2; + /** + * The 1.2.2 release and REST API version. + */ + public static final int RELEASE_VERSION_1_5_0 = 150; + public static final String API_VERSION_1_5_0 = "1.5.0"; + public static final String VERSION_HEADER_1_5_0 = VERSION_PREFIX + API_VERSION_1_5_0; + // Other examples: // public static final int RELEASE_VERSION_0_9_6 = 96; // public static final int RELEASE_VERSION_1_0_0 = 100; @@ -74,7 +81,7 @@ private ReleaseConstants() { /** * The current REST API version. */ - public static final String CURRENT_API_VERSION = API_VERSION_1_2_2; + public static final String CURRENT_API_VERSION = API_VERSION_1_5_0; public static final String CURRENT_VERSION_HEADER = VERSION_PREFIX + CURRENT_API_VERSION; } diff --git a/common/src/main/java/com/vmware/admiral/service/common/SslTrustCertificateService.java b/common/src/main/java/com/vmware/admiral/service/common/SslTrustCertificateService.java index 653269b25..590e26fca 100644 --- a/common/src/main/java/com/vmware/admiral/service/common/SslTrustCertificateService.java +++ b/common/src/main/java/com/vmware/admiral/service/common/SslTrustCertificateService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 VMware, Inc. All Rights Reserved. + * Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. @@ -17,7 +17,10 @@ import java.security.cert.X509Certificate; import java.util.concurrent.TimeUnit; +import com.esotericsoftware.kryo.serializers.VersionFieldSerializer; + import com.vmware.admiral.common.ManagementUriParts; +import com.vmware.admiral.common.serialization.ReleaseConstants; import com.vmware.admiral.common.util.ValidationUtils; import com.vmware.admiral.service.common.ConfigurationService.ConfigurationFactoryService; import com.vmware.admiral.service.common.ConfigurationService.ConfigurationState; @@ -102,6 +105,15 @@ public static class SslTrustCertificateState extends MultiTenantDocument { PropertyUsageOption.SINGLE_ASSIGNMENT }) public String subscriptionLink; + /** + * + */ + @Documentation(description = "Origin host uri from which the certificate is imported." + + " Cert could be a wildcard one, and valid for many hosts, this is just the" + + " initial source.") + @VersionFieldSerializer.Since(ReleaseConstants.RELEASE_VERSION_1_5_0) + public String origin; + public String getAlias() { return Service.getId(this.documentSelfLink); } diff --git a/common/src/main/java/com/vmware/admiral/service/common/SslTrustImportService.java b/common/src/main/java/com/vmware/admiral/service/common/SslTrustImportService.java index df5d15553..c3b6494aa 100644 --- a/common/src/main/java/com/vmware/admiral/service/common/SslTrustImportService.java +++ b/common/src/main/java/com/vmware/admiral/service/common/SslTrustImportService.java @@ -194,6 +194,7 @@ private SslTrustCertificateState createSslTrustCertificateState( sslTrustState.tenantLinks = request.tenantLinks; sslTrustState.documentSelfLink = SslTrustCertificateFactoryService .generateSelfLink(sslTrustState); + sslTrustState.origin = request.hostUri.toString(); return sslTrustState; } diff --git a/common/src/main/resources/i18n/messages.properties b/common/src/main/resources/i18n/messages.properties index 723a4c1c2..f54c2395f 100644 --- a/common/src/main/resources/i18n/messages.properties +++ b/common/src/main/resources/i18n/messages.properties @@ -151,6 +151,7 @@ compute.endpoint.adapter.enpoint.type.required=Endpoint type is required compute.endpoint.adapter.body.required=Body is required compute.endpoint.type.required=Endpoint or endpoint type must be specified compute.endpoint.link.or.type.only=Only one of endpoint link or endpoint type must be specified +compute.endpoint.exists=Endpoint already exists compute.body.required=body is required compute.content.unknown.yaml.type=Unknown YAML content type! Only Blueprint and Docker Compose v2 formats are supported. compute.content.deserialize.template=Failed to deserialize CompositeTemplate serialized content! diff --git a/compute/src/main/java/com/vmware/admiral/compute/EndpointCertificateUtil.java b/compute/src/main/java/com/vmware/admiral/compute/EndpointCertificateUtil.java index 6055487ea..8e53b8e1e 100644 --- a/compute/src/main/java/com/vmware/admiral/compute/EndpointCertificateUtil.java +++ b/compute/src/main/java/com/vmware/admiral/compute/EndpointCertificateUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 VMware, Inc. All Rights Reserved. + * Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. @@ -30,7 +30,7 @@ public class EndpointCertificateUtil { private static final Logger logger = Logger.getLogger(EndpointCertificateUtil.class.getName()); - static void validateSslTrust(Service sender, HostSpec hostSpec, Operation op, + public static void validateSslTrust(Service sender, HostSpec hostSpec, Operation op, Runnable callbackFunction) { if (hostSpec.sslTrust != null) { @@ -63,8 +63,9 @@ static void validateSslTrust(Service sender, HostSpec hostSpec, Operation op, if (e != null) { String message = String.format("Error connecting to %s : %s", hostSpec.uri.toString(), e.getMessage()); - LocalizableValidationException ex = new LocalizableValidationException(message, - "compute.add.host.connection.error", hostSpec.uri.toString(), e.getMessage()); + LocalizableValidationException ex = new LocalizableValidationException( + message, "compute.add.host.connection.error", + hostSpec.uri.toString(), e.getMessage()); ServiceErrorResponse rsp = Utils.toValidationErrorResponse(ex, op); logger.severe(rsp.message); op.setStatusCode(o.getStatusCode()); @@ -75,15 +76,18 @@ static void validateSslTrust(Service sender, HostSpec hostSpec, Operation op, // The SSL trust is not trusted self signed and must be accepted by the user // return to the user the certificate for confirmation. if (o.getStatusCode() == Operation.STATUS_CODE_OK) { - op.setBody(o.getBody(SslTrustCertificateState.class)); + SslTrustCertificateState body = o.getBody(SslTrustCertificateState.class); + // return in origin field the uri for which this certificate is + body.origin = hostSpec.uri.toString(); + op.setBody(body); op.setStatusCode(Operation.STATUS_CODE_OK); op.complete(); return; } + // certificate is trusted if (o.getStatusCode() == HttpURLConnection.HTTP_ACCEPTED) { - hostSpec.sslTrust = o.getBody - (SslTrustCertificateState.class); + hostSpec.sslTrust = o.getBody(SslTrustCertificateState.class); callbackFunction.run(); return; } @@ -123,4 +127,5 @@ private static void failOperation(HostSpec hostSpec, Operation op, Throwable t) hostSpec.uri, t.getMessage()); op.fail(new Exception(errMsg, t)); } + } diff --git a/compute/src/main/java/com/vmware/admiral/compute/HostSpec.java b/compute/src/main/java/com/vmware/admiral/compute/HostSpec.java index e77796a15..2c7dfdb92 100644 --- a/compute/src/main/java/com/vmware/admiral/compute/HostSpec.java +++ b/compute/src/main/java/com/vmware/admiral/compute/HostSpec.java @@ -16,7 +16,7 @@ import com.vmware.admiral.service.common.SslTrustCertificateService.SslTrustCertificateState; -abstract class HostSpec { +public abstract class HostSpec { /** * Boolean flag indicating whether the certificate of the host should be accepted or will diff --git a/compute/src/main/java/com/vmware/admiral/compute/pks/PKSCreateEndpointService.java b/compute/src/main/java/com/vmware/admiral/compute/pks/PKSCreateEndpointService.java new file mode 100644 index 000000000..b91789950 --- /dev/null +++ b/compute/src/main/java/com/vmware/admiral/compute/pks/PKSCreateEndpointService.java @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2018 VMware, Inc. All Rights Reserved. + * + * This product is licensed to you under the Apache License, Version 2.0 (the "License"). + * You may not use this product except in compliance with the License. + * + * This product may include a number of subcomponents with separate copyright notices + * and license terms. Your use of these subcomponents is subject to the terms and + * conditions of the subcomponent's license, as noted in the LICENSE file. + */ + +package com.vmware.admiral.compute.pks; + +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +import com.vmware.admiral.adapter.common.AdapterRequest; +import com.vmware.admiral.adapter.pks.PKSConstants; +import com.vmware.admiral.adapter.pks.PKSOperationType; +import com.vmware.admiral.common.ManagementUriParts; +import com.vmware.admiral.common.util.AssertUtil; +import com.vmware.admiral.common.util.QueryUtil; +import com.vmware.admiral.common.util.ServiceDocumentQuery; +import com.vmware.admiral.compute.EndpointCertificateUtil; +import com.vmware.admiral.compute.HostSpec; +import com.vmware.admiral.compute.pks.PKSEndpointService.Endpoint; +import com.vmware.admiral.service.common.ServiceTaskCallback; +import com.vmware.xenon.common.LocalizableValidationException; +import com.vmware.xenon.common.Operation; +import com.vmware.xenon.common.ServiceErrorResponse; +import com.vmware.xenon.common.StatelessService; +import com.vmware.xenon.common.UriUtils; +import com.vmware.xenon.common.Utils; +import com.vmware.xenon.services.common.QueryTask; + +public class PKSCreateEndpointService extends StatelessService { + + public static final String SELF_LINK = ManagementUriParts.PKS_CREATE_ENDPOINT; + + public static class EndpointSpec extends HostSpec { + + /** + * The endpoint exists and has to be updated. + */ + public Boolean isUpdateOperation; + public String acceptCertificateForHost; + public Endpoint endpoint; + + URI uaaUri; + URI apiUri; + + @Override + public boolean isSecureScheme() { + return uri == null || !UriUtils.HTTP_SCHEME.equals(uri.getScheme()); + } + + /** + * {@inheritDoc} + */ + @Override + public List getHostTenantLinks() { + return endpoint == null ? null : endpoint.tenantLinks; + } + + } + + @Override + public void handlePut(Operation op) { + if (!op.hasBody()) { + op.fail(new LocalizableValidationException("body is required", + "compute.body.required")); + return; + } + + EndpointSpec endpointSpec = op.getBody(EndpointSpec.class); + validate(endpointSpec); + + String query = op.getUri().getQuery(); + boolean validateConnection = query != null + && query.contains(ManagementUriParts.REQUEST_PARAM_VALIDATE_OPERATION_NAME); + + if (validateConnection) { + validateConnection(endpointSpec, op); + } else if (endpointSpec.isUpdateOperation != null && endpointSpec.isUpdateOperation) { + createOrUpdateEndpoint(endpointSpec, op); + } else { + QueryTask q = QueryUtil.buildPropertyQuery(Endpoint.class, + Endpoint.FIELD_NAME_API_ENDPOINT, + endpointSpec.endpoint.apiEndpoint); + + List tenantLinks = endpointSpec.getHostTenantLinks(); + if (tenantLinks != null) { + q.querySpec.query + .addBooleanClause(QueryUtil.addTenantGroupAndUserClause(tenantLinks)); + } + + AtomicBoolean found = new AtomicBoolean(false); + new ServiceDocumentQuery<>(getHost(), Endpoint.class) + .query(q, (r) -> { + if (r.hasException()) { + op.fail(r.getException()); + } else if (r.hasResult()) { + found.set(true); + op.fail(new LocalizableValidationException( + "Endpoint already exists", "compute.endpoint.exists")); + } else if (!found.get()) { + createOrUpdateEndpoint(endpointSpec, op); + } + }); + } + } + + private void validateSslTrust(EndpointSpec spec, Operation op, Runnable callback) { + boolean acceptAll = "*".equals(spec.acceptCertificateForHost); + + spec.uri = spec.uaaUri; + String uaaUri = spec.uaaUri.toString(); + spec.acceptCertificate = uaaUri.equals(spec.acceptCertificateForHost) || acceptAll; + EndpointCertificateUtil.validateSslTrust(this, spec, op, () -> { + spec.uri = spec.apiUri; + String apiUri = spec.apiUri.toString(); + spec.acceptCertificate = apiUri.equals(spec.acceptCertificateForHost) || acceptAll; + spec.sslTrust = null; + EndpointCertificateUtil.validateSslTrust(this, spec, op, callback); + }); + } + + private void validateConnection(EndpointSpec endpointSpec, Operation op) { + endpointSpec.acceptCertificate = false; + endpointSpec.acceptHostAddress = false; + endpointSpec.acceptCertificateForHost = null; + validateSslTrust(endpointSpec, op, + () -> pingEndpoint(endpointSpec, op, (ignored) -> completeOperationSuccess(op)) + ); + } + + private void createOrUpdateEndpoint(EndpointSpec endpointSpec, Operation op) { + if (endpointSpec.acceptHostAddress) { + if (endpointSpec.acceptCertificate) { + Operation o = Operation.createGet(null) + .setCompletion((completedOp, e) -> { + if (e != null) { + storeEndpoint(endpointSpec, op); + } else { + op.setStatusCode(completedOp.getStatusCode()); + op.transferResponseHeadersFrom(completedOp); + op.setBodyNoCloning(completedOp.getBodyRaw()); + op.complete(); + } + }); + validateSslTrust(endpointSpec, o, () -> storeEndpoint(endpointSpec, op)); + } else { + storeEndpoint(endpointSpec, op); + } + } else { + validateSslTrust(endpointSpec, op, () -> storeEndpoint(endpointSpec, op)); + } + } + + private void storeEndpoint(EndpointSpec endpointSpec, Operation op) { + if (endpointSpec.acceptHostAddress) { + doStoreEndpoint(endpointSpec, op); + } else { + // connect to endpoint + pingEndpoint(endpointSpec, op, (ignored) -> doStoreEndpoint(endpointSpec, op)); + } + } + + private void pingEndpoint(EndpointSpec spec, Operation op, Consumer callback) { + AdapterRequest request = new AdapterRequest(); + request.operationTypeId = PKSOperationType.LIST_PLANS.id; + request.serviceTaskCallback = ServiceTaskCallback.createEmpty(); + request.resourceReference = URI.create(""); + request.customProperties = new HashMap<>(6); + request.customProperties.put(PKSConstants.VALIDATE_CONNECTION, "true"); + request.customProperties.put(PKSConstants.CREDENTIALS_LINK, spec.endpoint.authCredentialsLink); + request.customProperties.put(Endpoint.FIELD_NAME_UAA_ENDPOINT, spec.uaaUri.toString()); + request.customProperties.put(Endpoint.FIELD_NAME_API_ENDPOINT, spec.apiUri.toString()); + + Operation patchOp = Operation + .createPatch(this, ManagementUriParts.ADAPTER_PKS) + .setBodyNoCloning(request) + .setCompletion((o, e) -> { + if (e != null) { + LocalizableValidationException localizedEx = + new LocalizableValidationException(e, + String.format("Unexpected error: %s", e.getMessage()), + "compute.unexpected.error", e.getMessage()); + + ServiceErrorResponse rsp = Utils.toValidationErrorResponse(localizedEx, op); + + logWarning("Error sending adapter request with type %s : %s. Cause: %s", + request.operationTypeId, rsp.message, Utils.toString(e)); + op.setStatusCode(o.getStatusCode()); + op.fail(localizedEx, rsp); + return; + } + + if (callback != null) { + callback.accept(null); + } + }); + + String languageHeader = op.getRequestHeader(Operation.ACCEPT_LANGUAGE_HEADER); + if (languageHeader != null) { + patchOp.addRequestHeader(Operation.ACCEPT_LANGUAGE_HEADER, languageHeader); + } + sendRequest(patchOp); + } + + private void doStoreEndpoint(EndpointSpec endpointSpec, Operation op) { + Endpoint e = endpointSpec.endpoint; + + if (e.id == null) { + e.id = UUID.randomUUID().toString(); + } + + Operation store; + if (e.documentSelfLink == null + || !e.documentSelfLink.startsWith(PKSEndpointService.FACTORY_LINK)) { + store = Operation + .createPost(getHost(), PKSEndpointService.FACTORY_LINK) + .addPragmaDirective(Operation.PRAGMA_DIRECTIVE_FORCE_INDEX_UPDATE); + } else { + store = Operation.createPut(getHost(), e.documentSelfLink); + } + if (e.creationTimeMicros == null) { + e.creationTimeMicros = Utils.getSystemNowMicrosUtc(); + } + + store.setBody(e) + .setCompletion((o, ex) -> { + if (ex != null) { + op.fail(ex); + return; + } + Endpoint endpoint = o.getBody(Endpoint.class); + String documentSelfLink = endpoint.documentSelfLink; + if (!documentSelfLink.startsWith(PKSEndpointService.FACTORY_LINK)) { + documentSelfLink = UriUtils.buildUriPath( + PKSEndpointService.FACTORY_LINK, documentSelfLink); + } + + op.addResponseHeader(Operation.LOCATION_HEADER, documentSelfLink); + completeOperationSuccess(op); + }) + .sendWith(this); + } + + private void completeOperationSuccess(Operation op) { + op.setStatusCode(HttpURLConnection.HTTP_NO_CONTENT); + op.setBody(null); + op.complete(); + } + + /* + try { + EndpointSpec endpointSpec = op.getBody(EndpointSpec.class); + validate(endpointSpec.endpoint); + + URI uaaUri = URI.create(endpointSpec.endpoint.uaaEndpoint); + HostSpec uaaHostSpec = new HostSpec() { + @Override + public boolean isSecureScheme() { + return UriUtils.HTTPS_SCHEME.equals(uaaUri.getScheme()); + } + + @Override + public List getHostTenantLinks() { + return null; + } + }; + uaaHostSpec.uri = uaaUri; + + EndpointCertificateUtil.validateSslTrust(this, uaaHostSpec, op, () -> { + URI apiUri = URI.create(endpointSpec.endpoint.apiEndpoint); + + HostSpec apiHostSpec = new HostSpec() { + @Override + public boolean isSecureScheme() { + return UriUtils.HTTPS_SCHEME.equals(apiUri.getScheme()); + } + + @Override + public List getHostTenantLinks() { + return null; + } + }; + apiHostSpec.uri = apiUri; + + EndpointCertificateUtil.validateSslTrust(this, apiHostSpec, op, () -> { + op.setBodyNoCloning(endpointSpec.endpoint); + op.complete(); + }); + }); + } catch (Throwable e) { + logSevere("Error creating PKS endpoint: %s. Error: %s", e.getMessage(), + Utils.toString(e)); + op.fail(e); + } + + */ + + private void validate(EndpointSpec spec) { + AssertUtil.assertNotNull(spec, "endpoint spec"); + AssertUtil.assertNotNull(spec.endpoint, "endpoint"); + AssertUtil.assertNotNullOrEmpty(spec.endpoint.uaaEndpoint, "UAA endpoint"); + AssertUtil.assertNotNullOrEmpty(spec.endpoint.apiEndpoint, "API endpoint"); + + URI uri; + Endpoint e = spec.endpoint; + + try { + logInfo("Parsing uaa endpoint: %s", e.uaaEndpoint); + uri = URI.create(e.uaaEndpoint); + } catch (Exception ex) { + throw new LocalizableValidationException("Invalid host address: " + e.uaaEndpoint, + "common.host.address.invalid", e.uaaEndpoint); + } + validateUri(uri); + spec.uaaUri = uri; + + try { + logInfo("Parsing api endpoint: %s", e.apiEndpoint); + uri = URI.create(e.apiEndpoint); + } catch (Exception ex) { + throw new LocalizableValidationException("Invalid host address: " + e.apiEndpoint, + "common.host.address.invalid", e.apiEndpoint); + } + validateUri(uri); + spec.apiUri = uri; + } + + private void validateUri(URI uri) { + if (!UriUtils.HTTPS_SCHEME.equalsIgnoreCase(uri.getScheme()) + && !UriUtils.HTTP_SCHEME.equalsIgnoreCase(uri.getScheme())) { + throw new LocalizableValidationException("Unsupported scheme: " + uri.getScheme(), + "common.unsupported.scheme", uri.getScheme()); + } + } + +} diff --git a/compute/src/main/java/com/vmware/admiral/compute/pks/PKSEndpointService.java b/compute/src/main/java/com/vmware/admiral/compute/pks/PKSEndpointService.java index 91df45aae..3392607d4 100644 --- a/compute/src/main/java/com/vmware/admiral/compute/pks/PKSEndpointService.java +++ b/compute/src/main/java/com/vmware/admiral/compute/pks/PKSEndpointService.java @@ -30,6 +30,8 @@ public class PKSEndpointService extends StatefulService { public static final String FACTORY_LINK = ManagementUriParts.PKS_ENDPOINTS; public static class Endpoint extends ResourceState { + public static final String FIELD_NAME_UAA_ENDPOINT = "uaaEndpoint"; + public static final String FIELD_NAME_API_ENDPOINT = "apiEndpoint"; @Documentation(description = "UAA endpoint address") @UsageOption(option = PropertyUsageOption.REQUIRED) @@ -59,9 +61,8 @@ public void handleCreate(Operation op) { return; } - Endpoint endpoint = op.getBody(Endpoint.class); - try { + Endpoint endpoint = op.getBody(Endpoint.class); validate(endpoint); op.complete(); } catch (Throwable e) { diff --git a/compute/src/main/java/com/vmware/admiral/host/HostInitComputeServicesConfig.java b/compute/src/main/java/com/vmware/admiral/host/HostInitComputeServicesConfig.java index 376f129d2..49d9ab859 100644 --- a/compute/src/main/java/com/vmware/admiral/host/HostInitComputeServicesConfig.java +++ b/compute/src/main/java/com/vmware/admiral/host/HostInitComputeServicesConfig.java @@ -82,6 +82,7 @@ import com.vmware.admiral.compute.kubernetes.service.ReplicationControllerService.ReplicationControllerState; import com.vmware.admiral.compute.kubernetes.service.ServiceEntityFactoryHandler; import com.vmware.admiral.compute.kubernetes.service.ServiceEntityHandler.ServiceState; +import com.vmware.admiral.compute.pks.PKSCreateEndpointService; import com.vmware.admiral.compute.pks.PKSEndpointService; import com.vmware.admiral.compute.util.DanglingDescriptionsCleanupService; import com.vmware.admiral.image.service.FavoriteImageFactoryService; @@ -128,7 +129,8 @@ public static void startServices(ServiceHost host, boolean startMockContainerHos DeploymentFactoryService.class, PodFactoryService.class, ServiceEntityFactoryHandler.class, - GenericKubernetesEntityFactoryService.class); + GenericKubernetesEntityFactoryService.class, + PKSCreateEndpointService.class); startServiceFactories(host, CaSigningCertService.class, GroupResourcePlacementService.class, diff --git a/compute/src/test/java/com/vmware/admiral/compute/container/SslTrustCertificateServiceTest.java b/compute/src/test/java/com/vmware/admiral/compute/container/SslTrustCertificateServiceTest.java index 78168ccc1..46fb7572f 100644 --- a/compute/src/test/java/com/vmware/admiral/compute/container/SslTrustCertificateServiceTest.java +++ b/compute/src/test/java/com/vmware/admiral/compute/container/SslTrustCertificateServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 VMware, Inc. All Rights Reserved. + * Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. @@ -36,6 +36,7 @@ import com.vmware.xenon.common.UriUtils; public class SslTrustCertificateServiceTest extends ComputeBaseTest { + private static final String HTTPS_HOST_COM = "https://host.com"; private String sslTrust1; private String sslTrust2; private SslTrustCertificateState sslTrustCert; @@ -46,6 +47,7 @@ public void setUp() throws Throwable { sslTrust2 = CommonTestStateFactory.getFileContent("test_ssl_trust2.PEM").trim(); sslTrustCert = new SslTrustCertificateState(); sslTrustCert.certificate = sslTrust1; + sslTrustCert.origin = HTTPS_HOST_COM; waitForServiceAvailability(SslTrustCertificateService.FACTORY_LINK); waitForServiceAvailability(ContainerFactoryService.SELF_LINK); @@ -119,11 +121,13 @@ public void testIdempotentPOST() throws Throwable { SslTrustCertificateState sslTrustCert1 = new SslTrustCertificateState(); sslTrustCert1.certificate = sslTrust1; sslTrustCert1.subscriptionLink = null; + sslTrustCert1.origin = HTTPS_HOST_COM; sslTrustCert1 = doPost(sslTrustCert1, SslTrustCertificateService.FACTORY_LINK); SslTrustCertificateState sslTrustCert2 = new SslTrustCertificateState(); sslTrustCert2.certificate = sslTrust1; sslTrustCert2.subscriptionLink = "subscription-link"; + sslTrustCert2.origin = HTTPS_HOST_COM; sslTrustCert2 = doPost(sslTrustCert2, SslTrustCertificateService.FACTORY_LINK); sslTrustCert = getDocument(SslTrustCertificateState.class, @@ -216,6 +220,7 @@ private void postForValidation(SslTrustCertificateState state) throws Throwable } private void validateCertProperties(SslTrustCertificateState state) throws Exception { + assertEquals(HTTPS_HOST_COM, state.origin); X509Certificate[] certificates = CertificateUtil.createCertificateChain(state.certificate); for (X509Certificate cert : certificates) { diff --git a/compute/src/test/java/com/vmware/admiral/compute/pks/PKSCreateEndpointServiceTest.java b/compute/src/test/java/com/vmware/admiral/compute/pks/PKSCreateEndpointServiceTest.java new file mode 100644 index 000000000..5e80f7caf --- /dev/null +++ b/compute/src/test/java/com/vmware/admiral/compute/pks/PKSCreateEndpointServiceTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018 VMware, Inc. All Rights Reserved. + * + * This product is licensed to you under the Apache License, Version 2.0 (the "License"). + * You may not use this product except in compliance with the License. + * + * This product may include a number of subcomponents with separate copyright notices + * and license terms. Your use of these subcomponents is subject to the terms and + * conditions of the subcomponent's license, as noted in the LICENSE file. + */ + +package com.vmware.admiral.compute.pks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.function.Consumer; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.vmware.admiral.common.DeploymentProfileConfig; +import com.vmware.admiral.compute.container.ComputeBaseTest; +import com.vmware.admiral.compute.pks.PKSCreateEndpointService.EndpointSpec; +import com.vmware.admiral.compute.pks.PKSEndpointService.Endpoint; +import com.vmware.xenon.common.LocalizableValidationException; +import com.vmware.xenon.common.Operation; +import com.vmware.xenon.common.ServiceErrorResponse; +import com.vmware.xenon.common.test.TestRequestSender; + +public class PKSCreateEndpointServiceTest extends ComputeBaseTest { + + private TestRequestSender sender; + + @BeforeClass + public static void beforeClass() { + DeploymentProfileConfig.getInstance().setTest(false); + } + + @AfterClass + public static void afterClass() { + DeploymentProfileConfig.getInstance().setTest(true); + } + + @Before + public void setUp() throws Throwable { + waitForServiceAvailability(PKSEndpointService.FACTORY_LINK, + PKSCreateEndpointService.SELF_LINK); + sender = host.getTestRequestSender(); + } + + @Test + public void testCreate() throws Throwable { + Endpoint endpoint = new Endpoint(); + endpoint.apiEndpoint = "https://localhost"; + endpoint.uaaEndpoint = "https://localhost"; + + EndpointSpec endpointSpec = new EndpointSpec(); + endpointSpec.acceptHostAddress = true; + endpointSpec.acceptCertificate = true; + endpointSpec.endpoint = endpoint; + + Endpoint createdEndpoint = createEndpoint(endpointSpec); + + endpoint = new Endpoint(); + endpoint.apiEndpoint = "https://localhost"; + endpoint.uaaEndpoint = "https://localhost"; + createEndpointExpectFailure(endpointSpec, e -> { + e.getErrorCode(); + }); + } + + @Test + public void testUpdate() throws Throwable { + Endpoint endpoint = new Endpoint(); + endpoint.apiEndpoint = "https://localhost"; + endpoint.uaaEndpoint = "https://localhost"; + + EndpointSpec endpointSpec = new EndpointSpec(); + endpointSpec.acceptHostAddress = true; + endpointSpec.acceptCertificate = true; + endpointSpec.endpoint = endpoint; + + createEndpoint(endpointSpec); + + endpoint.uaaEndpoint = "http://some-other-host"; + endpointSpec.isUpdateOperation = true; + + Endpoint updatedEndpoint = createEndpoint(endpointSpec); + assertEquals(endpoint.uaaEndpoint, updatedEndpoint.uaaEndpoint); + } + + private Endpoint createEndpoint(EndpointSpec endpointSpec) throws Throwable { + Operation o = Operation + .createPut(host, PKSCreateEndpointService.SELF_LINK) + .setBodyNoCloning(endpointSpec); + + o = sender.sendAndWait(o); + assertNotNull(o); + String locationHeader = o.getResponseHeader(Operation.LOCATION_HEADER); + assertNotNull(locationHeader); + + Endpoint result = getDocumentNoWait(Endpoint.class, locationHeader); + + assertNotNull(result); + assertNotNull(result.documentSelfLink); + assertEquals(endpointSpec.endpoint.uaaEndpoint, result.uaaEndpoint); + assertEquals(endpointSpec.endpoint.apiEndpoint, result.apiEndpoint); + + return result; + } + + private void createEndpointExpectFailure(EndpointSpec e, Consumer consumer) { + Operation o = Operation + .createPut(host, PKSCreateEndpointService.SELF_LINK) + .setBodyNoCloning(e); + TestRequestSender.FailureResponse failure = sender.sendAndWaitFailure(o); + assertTrue(failure.failure instanceof LocalizableValidationException); + ServiceErrorResponse errorResponse = failure.op.getBody(ServiceErrorResponse.class); + assertNotNull(errorResponse); + + consumer.accept(errorResponse); + } + +} \ No newline at end of file diff --git a/compute/src/test/java/com/vmware/admiral/compute/pks/PKSEndpointServiceTest.java b/compute/src/test/java/com/vmware/admiral/compute/pks/PKSEndpointServiceTest.java index 5ced981a7..19819aa07 100644 --- a/compute/src/test/java/com/vmware/admiral/compute/pks/PKSEndpointServiceTest.java +++ b/compute/src/test/java/com/vmware/admiral/compute/pks/PKSEndpointServiceTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.function.BiConsumer; @@ -66,7 +67,6 @@ public void testCreate() { assertEquals(Operation.STATUS_CODE_BAD_REQUEST, ser.statusCode); assertTrue(ser.message.startsWith("Unsupported scheme, must be http or https")); }); - } @Test @@ -96,7 +96,19 @@ public void testUpdate() { assertEquals(patch2.apiEndpoint, r.apiEndpoint); assertEquals(endpoint1.uaaEndpoint, r.uaaEndpoint); }); + } + + @Test + public void testDelete() throws Throwable { + Endpoint endpoint = new Endpoint(); + endpoint.apiEndpoint = "http://localhost"; + endpoint.uaaEndpoint = "https://localhost"; + + endpoint = createEndpoint(endpoint); + delete(endpoint.documentSelfLink); + endpoint = getDocumentNoWait(Endpoint.class, endpoint.documentSelfLink); + assertNull(endpoint); } private Endpoint createEndpoint(Endpoint endpoint) { diff --git a/test-integration/src/test/java/com/vmware/admiral/test/integration/SslTrustImportServiceIT.java b/test-integration/src/test/java/com/vmware/admiral/test/integration/SslTrustImportServiceIT.java index 6d4813c7a..9126ddfa0 100644 --- a/test-integration/src/test/java/com/vmware/admiral/test/integration/SslTrustImportServiceIT.java +++ b/test-integration/src/test/java/com/vmware/admiral/test/integration/SslTrustImportServiceIT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 VMware, Inc. All Rights Reserved. + * Copyright (c) 2016-2018 VMware, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. @@ -51,7 +51,7 @@ public void setUp() throws Throwable { } @Test - public void testImportPublicCertificateShouldReturAccepted() throws Throwable { + public void testImportPublicCertificateShouldReturnAccepted() throws Throwable { request.hostUri = URI.create(certTrustedUrl);// public trusted certificate Operation response = putRequest(request); @@ -78,6 +78,7 @@ public void testImportSelfSingedCertShouldReturnImportCertForConfirmation() thro assertEquals(request.hostUri.getHost(), sslTrustState.commonName); assertEquals(request.hostUri.getHost(), sslTrustState.issuerName); + assertEquals(certSelfSignedUrl, sslTrustState.origin); } @Test @@ -107,6 +108,7 @@ public void testImportSelfSingedCertShouldBeStoredWhenAccepted() throws Throwabl assertEquals(request.hostUri.getHost(), sslTrustState.commonName); assertEquals(request.hostUri.getHost(), sslTrustState.issuerName); + assertEquals(certSelfSignedUrl, sslTrustState.origin); assertEquals(request.tenantLinks.size(), sslTrustState.tenantLinks.size()); Collections.sort(request.tenantLinks);