diff --git a/multiapps-controller-client/pom.xml b/multiapps-controller-client/pom.xml index acfb0201c9..ee32a60a98 100644 --- a/multiapps-controller-client/pom.xml +++ b/multiapps-controller-client/pom.xml @@ -85,17 +85,19 @@ value provided - - - org.cloudfoundry - cloudfoundry-client-reactor - - + io.projectreactor.netty reactor-netty + + + + commons-logging + commons-logging + ${commons-logging.version} + @@ -140,30 +142,6 @@ - - org.cloudfoundry - cloudfoundry-client-reactor - ${cloudfoundry-client.version} - - - - org.slf4j - jcl-over-slf4j - - - - com.fasterxml.jackson.core - jackson-databind - - - commons-logging diff --git a/multiapps-controller-client/src/main/java/module-info.java b/multiapps-controller-client/src/main/java/module-info.java index 4201bc0a0b..dfd1045713 100644 --- a/multiapps-controller-client/src/main/java/module-info.java +++ b/multiapps-controller-client/src/main/java/module-info.java @@ -12,7 +12,6 @@ exports org.cloudfoundry.multiapps.controller.client.facade.util; exports org.cloudfoundry.multiapps.controller.client.facade.dto; - requires transitive org.cloudfoundry.client; requires spring.security.oauth2.core; requires transitive spring.web; @@ -20,9 +19,7 @@ requires org.apache.commons.collections4; requires org.apache.commons.io; requires org.apache.commons.logging; - requires org.cloudfoundry.client.reactor; requires org.cloudfoundry.multiapps.common; - requires org.cloudfoundry.util; requires org.slf4j; requires java.net.http; requires spring.core; @@ -41,5 +38,7 @@ requires static java.compiler; requires static jakarta.inject; requires static org.immutables.value; + requires io.netty.codec; + requires java.desktop; } \ No newline at end of file diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Constants.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Constants.java new file mode 100644 index 0000000000..705fc88798 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Constants.java @@ -0,0 +1,38 @@ +package org.cloudfoundry.multiapps.controller; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +public class Constants { + + public static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofMinutes(1); + public static final Duration JOB_POLL_MIN_INTERVAL = Duration.ofSeconds(1); + public static final Duration JOB_POLL_MAX_INTERVAL = Duration.ofSeconds(15); + public static final Duration DELETE_JOB_TIMEOUT = Duration.ofMinutes(5); + public static final Duration BINDING_OPERATIONS_TIMEOUT = Duration.ofMinutes(10); + public static final long PACKAGE_UPLOAD_JOB_POLLING_PERIOD = TimeUnit.SECONDS.toMillis(5); + + public static final int DEFAULT_CONNECTION_POOL_SIZE = 192; + public static final int UNDEFINED_PORT = -1; + public static final int MAX_CONCURRENT_PAGES = 256; + public static final int DEFAULT_CONCURRENT_TASKS = 256; + public static final int MAX_CHAR_LENGTH_FOR_PARAMS_IN_REQUEST = 4000; + + public static final String CONNECTION_POOL_NAME = "cf-controller-client"; + public static final String LOOP_RESOURCES_SUFFIX = "-loop-resources"; + public static final String CF_API_V3 = "/v3"; + public static final String HREF = "href"; + public static final String CLOUD_CONTROLLER_CF_ROOT_DOCUMENT_NAME = "cloud_controller_v3"; + public static final String LOG_CACHE_CF_ROOT_DOCUMENT_NAME = "log_cache"; + public static final String API_HOST_PREFIX = "api."; + public static final String LOG_CACHE_PREFIX = "log-cache."; + public static final String ROOT_DOCUMENT_LINKS_LIST = "links"; + + public static final String EMPTY_STRING = ""; + public static final String COLON = ":"; + public static final String PROTOCOL_SEPARATOR = "://"; + + public static final String PACKAGE_LINK = "package"; + public static final String WEB_PROCESS_TYPE = "web"; + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Messages.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Messages.java index af0d03d5f0..8705623590 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Messages.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/Messages.java @@ -3,9 +3,52 @@ public class Messages { // INFO messages - public static final String WAITING_MS_BEFORE_RETRYING_WITH_TIMEOUT_OF_MS = "Waiting: {} ms before retrying with timeout of: {} ms"; + public static final String WAITING_MS_BEFORE_RETRYING_WITH_TIMEOUT_OF_MS = "Waiting: {} ms before retrying with timeout of: {} ms."; public static final String RATE_LIMITED_BY_CC_WAITING_S = "CC returned 429 with Retry-After: {} s. Waiting {} s (capped) before retrying."; public static final String RATE_LIMITED_BY_CC_NO_HEADER_WAITING_MS = "CC returned 429 without Retry-After header. Waiting {} ms before retrying."; public static final String RANDOM_WAIT_BEFORE_RETRY_MS = "Waiting {} ms (randomized) before retrying failed CC operation."; + // Exception messages + public static final String ERROR_OCCURRED_SETTING_UP_DEFAULT_SSL_CONTEXT = "An error occurred while setting up the default SSLContext."; + public static final String ERROR_OCCURRED_SETTING_UP_ALWAYS_APPROVING_SSL_CONTEXT = "An error occurred while setting up the always approving SSLContext."; + public static final String COULD_NOT_DETERMINE_API_ORIGIN_FROM_0 = "Could not determine the API origin from \"{0}\"."; + public static final String COULD_NOT_RESOLVE_URL_FROM_0_1 = "Could not resolve URL from \"{0}\" \"{1}\"."; + public static final String COULD_NOT_RESOLVE_LOG_CACHE_URL_FROM_0 = "Could not derive the log-cache URL from \"{0}\"."; + public static final String CF_ROOT_DOCUMENT_REQUEST_TO_0_RETURNED_1 = "CF root request to \"{0}\" returned \"{1}\"."; + public static final String INTERRUPTED_WHILE_CALLING_THE_CF_ROOT_URL_AT_0 = "Interrupted while calling the CF root URL at \"{0}\"."; + public static final String FAILED_TO_CALL_THE_CF_ROOT_AT_0_WITH_1 = "Failed to call the CF root at \"{0}\" with: \"{1}\"."; + public static final String SERVICE_KEY_0_NOT_FOUND = "Service key \"{0}\" not found."; + public static final String SERVICE_INSTANCE_0_NOT_FOUND = "Service instance \"{0}\" not found."; + public static final String SERVICE_BROKER_0_NOT_FOUND = "Service broker \"{0}\" not found."; + public static final String APPLICATION_0_NOT_FOUND = "Application \"{0}\" not found."; + public static final String DOMAIN_0_NOT_FOUND = "Domain \"{0}\" not found."; + public static final String SERVICE_PLAN_0_NOT_FOUND = "Service plan \"{0}\" not found."; + public static final String STACK_0_NOT_FOUND = "Stack \"{0}\" not found."; + public static final String ORGANISATION_0_NOT_FOUND = "Organization \"{0}\" not found."; + public static final String SPACE_WITH_GUID_0_NOT_FOUND = "Space with GUID \"{0}\" not found."; + public static final String APPLICATION_WITH_GUID_0_DOES_NOT_HAVE_A_DROPLET = "Application with guid \"{0}\" does not have a droplet"; + public static final String HOST_0_NOT_FOUND_FOR_DOMAIN_1 = "Host \"{0}\" not found for domain \"{1}\"."; + public static final String UNABLE_TO_0_WITHOUT_SPECIFYING_ORGANIZATION_AND_SPACE_TO_USE = "Unable to \"{0}\" without specifying organization and space to use."; + public static final String DOMAIN_0_NOT_FOUND_FOR_URI_1 = "Domain \"{0}\" not found for URI \"{1}\""; + public static final String SERVICE_BINDING_BETWEEN_SERVICE_WITH_GUID_0_AND_APPLICATION_WITH_GUID_1_NOT_FOUND = "Service binding between service with GUID \"{0}\" and application with GUID \"{1}\" not found."; + public static final String SERVICE_PLAN_WITH_GUID_0_NOT_AVAILABLE_FOR_SERVICE_INSTANCE_1 = "Service plan with guid \"{0}\" is not available for service instance \"{1}\"."; + public static final String NO_SERVICE_PLAN_FOUND = "Service plan with guid \"{0}\" for service instance with name \"{1}\" was not found."; + public static final String SERVICE_OFFERING_WITH_GUID_0_IS_NOT_AVAILABLE = "Service offering with guid \"{0}\" is not available."; + public static final String SERVICE_OFFERING_WITH_GUID_0_NOT_FOUND = "Service offering with guid \"{0}\" not found."; + public static final String TARGET_SPACE_REQUIRED_TO_CREATE_AN_APPLICATION = "A target space is required to create an application."; + public static final String JOB_0_DID_NOT_COMPLETE_WITHIN_1 = "Job \"{0}\" did not complete within \"{1}\""; + public static final String ORGANIZATION_WITH_GUID_0_NOT_FOUND = "Organization with GUID \"{0}\" not found."; + public static final String SPACE_0_NOT_FOUND_IN_ORGANIZATION_1 = "Space \"{0}\" not found in organization \"{1}\""; + public static final String INTERRUPTED_WHILE_POLLING_ASYNC_JOB_0 = "Interrupted while polling an async job: \"{0}\""; + public static final String UNKNOWN_JOB_STATE_0 = "Unknown job state: \"{0}\""; + public static final String UNKNOWN_SERVICE_INSTANCE_TYPE_0 = "Unknown service instance type: \"{0}\""; + public static final String UNKNOWN_USER_ROLE_0 = "Unknown user role: \"{0}\""; + + //Status texts + public static final String NOT_FOUND = "Not Found"; + public static final String JOB_TIMEOUT = "Job Timeout"; + public static final String JOB_FAILED = "Job Failed"; + public static final String BAD_REQUEST = "Bad Request"; + public static final String FORBIDDEN = "Forbidden"; + } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java index acffeeb34c..94da22f577 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java @@ -10,7 +10,6 @@ import java.util.function.Function; import java.util.function.Supplier; -import org.cloudfoundry.client.v3.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.ApplicationServicesUpdateCallback; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClientImpl; @@ -35,6 +34,7 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; import org.cloudfoundry.multiapps.controller.client.facade.domain.Upload; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/AllowNulls.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/AllowNulls.java new file mode 100644 index 0000000000..944e54edf6 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/AllowNulls.java @@ -0,0 +1,11 @@ +package org.cloudfoundry.multiapps.controller.client.facade; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface AllowNulls { +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java index 2d8f04ccdf..89f8dec346 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java index ca0386040c..e016da176c 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java @@ -11,8 +11,6 @@ import java.util.UUID; import java.util.function.Supplier; -import org.cloudfoundry.AbstractCloudFoundryException; -import org.cloudfoundry.client.v3.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; @@ -32,6 +30,7 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; import org.cloudfoundry.multiapps.controller.client.facade.domain.Upload; @@ -40,7 +39,6 @@ import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudControllerRestClient; import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudControllerRestClientFactory; import org.cloudfoundry.multiapps.controller.client.facade.rest.ImmutableCloudControllerRestClientFactory; -import org.springframework.http.HttpStatus; import org.springframework.util.Assert; /** @@ -659,16 +657,9 @@ private void handleExceptions(Runnable runnable) { } private T handleExceptions(Supplier runnable) { - try { - return runnable.get(); - } catch (AbstractCloudFoundryException e) { - throw convertV3ClientException(e); - } - } - - private CloudOperationException convertV3ClientException(AbstractCloudFoundryException e) { - HttpStatus httpStatus = HttpStatus.valueOf(e.getStatusCode()); - return new CloudOperationException(httpStatus, httpStatus.getReasonPhrase(), e.getMessage(), e); + // The underlying CloudControllerRestClient already surfaces failures as CloudOperationException + // (via CloudControllerResponseErrorHandler), so no exception translation is needed here. + return runnable.get(); } } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/CloudFoundryClientFactory.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/CloudFoundryClientFactory.java deleted file mode 100644 index 5461f765a8..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/CloudFoundryClientFactory.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.text.MessageFormat; -import java.time.Duration; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; - -import org.cloudfoundry.client.CloudFoundryClient; -import org.cloudfoundry.client.v3.organizations.OrganizationsV3; -import org.cloudfoundry.client.v3.spaces.SpacesV3; -import org.cloudfoundry.reactor.ConnectionContext; -import org.cloudfoundry.reactor.DefaultConnectionContext; -import org.cloudfoundry.reactor.client.ReactorCloudFoundryClient; -import org.cloudfoundry.reactor.client.v3.organizations.ReactorOrganizationsV3; -import org.cloudfoundry.reactor.client.v3.spaces.ReactorSpacesV3; -import org.immutables.value.Value; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; - -import org.cloudfoundry.multiapps.controller.client.facade.CloudException; -import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; -import org.cloudfoundry.multiapps.controller.client.facade.Messages; -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; -import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudSpaceClient; -import org.cloudfoundry.multiapps.controller.client.facade.util.CloudUtil; -import org.cloudfoundry.multiapps.controller.client.facade.util.JsonUtil; - -import reactor.core.publisher.Mono; - -@Value.Immutable -public abstract class CloudFoundryClientFactory { - - private static final Logger LOGGER = LoggerFactory.getLogger(CloudFoundryClientFactory.class); - - static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() - .executor(Executors.newSingleThreadExecutor()) - .followRedirects(HttpClient.Redirect.NORMAL) - .connectTimeout(Duration.ofMinutes(10)) - .build(); - - private final Map connectionContextCache = new ConcurrentHashMap<>(); - - public abstract Optional getSslHandshakeTimeout(); - - public abstract Optional getConnectTimeout(); - - public abstract Optional getConnectionPoolSize(); - - public abstract Optional getThreadPoolSize(); - - public abstract Optional getResponseTimeout(); - - public CloudFoundryClient createClient(URL controllerUrl, OAuthClient oAuthClient, Map requestTags) { - return ReactorCloudFoundryClient.builder() - .connectionContext(getOrCreateConnectionContext(controllerUrl.getHost())) - .tokenProvider(oAuthClient.getTokenProvider()) - .requestTags(requestTags) - .build(); - } - - public LogCacheClient createLogCacheClient(URL controllerUrl, OAuthClient oAuthClient, Map requestTags) { - return new LogCacheClient(oAuthClient, requestTags, getOrCreateConnectionContext(controllerUrl.getHost())); - } - - @SuppressWarnings("unchecked") - private Map callCfRoot(URL controllerUrl, Map requestTags) { - HttpResponse response; - try { - HttpRequest request = buildCfRootRequest(controllerUrl, requestTags); - LOGGER.info(MessageFormat.format(Messages.CALLING_CF_ROOT_0_TO_ACCESS_LOG_CACHE_URL, controllerUrl)); - response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() / 100 != 2) { - var status = HttpStatus.valueOf(response.statusCode()); - throw new CloudOperationException(status, status.getReasonPhrase(), response.body()); - } - LOGGER.info(Messages.CF_ROOT_REQUEST_FINISHED); - } catch (InterruptedException | URISyntaxException | IOException e) { - throw new CloudException(e.getMessage(), e); - } - var map = JsonUtil.convertJsonToMap(response.body()); - return (Map) map.get("links"); - } - - private HttpRequest buildCfRootRequest(URL controllerUrl, Map requestTags) throws URISyntaxException { - var requestBuilder = HttpRequest.newBuilder() - .GET() - .uri(controllerUrl.toURI()) - .timeout(Duration.ofMinutes(5)); - requestTags.forEach(requestBuilder::header); - return requestBuilder.build(); - } - - public CloudSpaceClient createSpaceClient(URL controllerUrl, OAuthClient oAuthClient, Map requestTags) { - String v3Api; - try { - var links = CloudUtil.executeWithRetry(() -> callCfRoot(controllerUrl, requestTags)); - @SuppressWarnings("unchecked") - var ccv3 = (Map) links.get("cloud_controller_v3"); - v3Api = (String) ccv3.get("href"); - } catch (CloudException e) { - LOGGER.warn(MessageFormat.format(Messages.CALL_TO_0_FAILED_WITH_1, controllerUrl.toString(), e.getMessage()), e); - v3Api = controllerUrl + "/v3"; - } - var spacesV3 = createV3SpacesClient(controllerUrl, v3Api, oAuthClient, requestTags); - var orgsV3 = createV3OrgsClient(controllerUrl, v3Api, oAuthClient, requestTags); - return new CloudSpaceClient(spacesV3, orgsV3); - } - - private SpacesV3 createV3SpacesClient(URL controllerUrl, String v3Api, OAuthClient oAuthClient, Map requestTags) { - return new ReactorSpacesV3(getOrCreateConnectionContext(controllerUrl.getHost()), - Mono.just(v3Api), - oAuthClient.getTokenProvider(), - requestTags); - } - - private OrganizationsV3 createV3OrgsClient(URL controllerUrl, String v3Api, OAuthClient oAuthClient, Map requestTags) { - return new ReactorOrganizationsV3(getOrCreateConnectionContext(controllerUrl.getHost()), - Mono.just(v3Api), - oAuthClient.getTokenProvider(), - requestTags); - } - - public ConnectionContext getOrCreateConnectionContext(String controllerApiHost) { - return connectionContextCache.computeIfAbsent(controllerApiHost, this::createConnectionContext); - } - - private ConnectionContext createConnectionContext(String controllerApiHost) { - DefaultConnectionContext.Builder builder = DefaultConnectionContext.builder() - .apiHost(controllerApiHost); - getSslHandshakeTimeout().ifPresent(builder::sslHandshakeTimeout); - getConnectTimeout().ifPresent(builder::connectTimeout); - getConnectionPoolSize().ifPresent(builder::connectionPoolSize); - getThreadPoolSize().ifPresent(builder::threadPoolSize); - builder.additionalHttpClientConfiguration(this::getAdditionalHttpClientConfiguration); - return builder.build(); - } - - private reactor.netty.http.client.HttpClient getAdditionalHttpClientConfiguration(reactor.netty.http.client.HttpClient client) { - var clientWithOptions = client; - if (getResponseTimeout().isPresent()) { - clientWithOptions = clientWithOptions.responseTimeout(getResponseTimeout().get()); - } - - return clientWithOptions; - } -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/LogCacheClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/LogCacheClient.java index ea8d519b3f..304a80dff8 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/LogCacheClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/LogCacheClient.java @@ -1,19 +1,5 @@ package org.cloudfoundry.multiapps.controller.client.facade.adapters; -import org.cloudfoundry.multiapps.controller.client.facade.CloudException; -import org.cloudfoundry.multiapps.controller.client.facade.Messages; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ApplicationLog; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableApplicationLog; -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; -import org.cloudfoundry.logcache.v1.Envelope; -import org.cloudfoundry.logcache.v1.EnvelopeType; -import org.cloudfoundry.logcache.v1.ReadRequest; -import org.cloudfoundry.logcache.v1.ReadResponse; -import org.cloudfoundry.reactor.ConnectionContext; -import org.cloudfoundry.reactor.logcache.v1.ReactorLogCacheClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.nio.charset.StandardCharsets; import java.text.MessageFormat; import java.time.Duration; @@ -24,77 +10,72 @@ import java.util.Base64; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.UUID; +import org.cloudfoundry.multiapps.controller.client.facade.CloudException; +import org.cloudfoundry.multiapps.controller.client.facade.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ApplicationLog; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableApplicationLog; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.LogCacheReadResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.client.RestClient; + public class LogCacheClient { private static final Logger LOGGER = LoggerFactory.getLogger(LogCacheClient.class); private static final String SOURCE_TYPE_KEY_NAME = "source_type"; private static final int MAX_LOG_COUNT = 1000; - private final OAuthClient oAuthClient; - private final Map requestTags; - private final ConnectionContext connectionContext; - public LogCacheClient(OAuthClient oAuthClient, Map requestTags, ConnectionContext connectionContext) { - this.oAuthClient = oAuthClient; - this.requestTags = requestTags; - this.connectionContext = connectionContext; + private final RestClient restClient; + + public LogCacheClient(RestClient restClient) { + this.restClient = restClient; } public List getRecentLogs(UUID applicationGuid, LocalDateTime offset) { LOGGER.info(Messages.TRYING_TO_GET_APP_LOGS); - org.cloudfoundry.logcache.v1.LogCacheClient logCacheClient = createReactorLogCacheClient(); - ReadResponse applicationLogsResponse = readApplicationLogs(logCacheClient, applicationGuid, offset); + LogCacheReadResponse response = readApplicationLogs(applicationGuid, offset); - if (applicationLogsResponse != null) { - LOGGER.info(Messages.APP_LOGS_WERE_FETCHED_SUCCESSFULLY); - return applicationLogsResponse.getEnvelopes() - .getBatch() - .stream() - .map(this::mapToAppLog) - // we use a linked list so that the log messages can be a LIFO sequence - // that way, we avoid unnecessary sorting and copying to and from another collection/array - .collect(LinkedList::new, LinkedList::addFirst, LinkedList::addAll); - } else { + if (response == null) { throw new CloudException(MessageFormat.format(Messages.FAILED_TO_FETCH_APP_LOGS_FOR_APP, applicationGuid)); } + LOGGER.info(Messages.APP_LOGS_WERE_FETCHED_SUCCESSFULLY); + return response.batch() + .stream() + .map(this::mapToAppLog) + // we use a linked list so that the log messages can be a LIFO sequence + // that way, we avoid unnecessary sorting and copying to and from another collection/array + .collect(LinkedList::new, LinkedList::addFirst, LinkedList::addAll); } - private ReactorLogCacheClient createReactorLogCacheClient() { - return ReactorLogCacheClient.builder() - .requestTags(requestTags) - .connectionContext(connectionContext) - .tokenProvider(oAuthClient.getTokenProvider()) - .build(); - } - - private ReadResponse readApplicationLogs(org.cloudfoundry.logcache.v1.LogCacheClient logCacheClient, UUID applicationGuid, - LocalDateTime offset) { + private LogCacheReadResponse readApplicationLogs(UUID applicationGuid, LocalDateTime offset) { var instant = offset.toInstant(ZoneOffset.UTC); var secondsInNanos = Duration.ofSeconds(instant.getEpochSecond()) .toNanos(); - return logCacheClient.read(ReadRequest.builder() - .envelopeType(EnvelopeType.LOG) - .sourceId(applicationGuid.toString()) - .descending(Boolean.TRUE) - .limit(MAX_LOG_COUNT) - .startTime(secondsInNanos + instant.getNano() + 1) - .build()) - .block(); + long startTime = secondsInNanos + instant.getNano() + 1; + + return restClient.get() + .uri(uriBuilder -> uriBuilder.path("/api/v1/read/{sourceId}") + .queryParam("envelope_types", "LOG") + .queryParam("descending", Boolean.TRUE) + .queryParam("limit", MAX_LOG_COUNT) + .queryParam("start_time", startTime) + .build(applicationGuid.toString())) + .retrieve() + .body(LogCacheReadResponse.class); } - private ApplicationLog mapToAppLog(Envelope envelope) { + private ApplicationLog mapToAppLog(LogCacheReadResponse.Envelope envelope) { return ImmutableApplicationLog.builder() - .applicationGuid(envelope.getSourceId()) - .message(decodeLogPayload(envelope.getLog() - .getPayload())) - .timestamp(fromLogTimestamp(envelope.getTimestamp())) - .messageType(fromLogMessageType(envelope.getLog() - .getType() - .getValue())) - .sourceName(envelope.getTags() + .applicationGuid(envelope.sourceId()) + .message(decodeLogPayload(envelope.log() + .payload())) + .timestamp(fromLogTimestamp(envelope.timestamp())) + .messageType(fromLogMessageType(envelope.log() + .type())) + .sourceName(envelope.tags() .get(SOURCE_TYPE_KEY_NAME)) .build(); } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/OAuthTokenProvider.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/OAuthTokenProvider.java deleted file mode 100644 index ff5def4c97..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/OAuthTokenProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.reactor.ConnectionContext; -import org.cloudfoundry.reactor.TokenProvider; - -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo; -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; - -import reactor.core.publisher.Mono; - -public class OAuthTokenProvider implements TokenProvider { - - private final OAuthClient oAuthClient; - - public OAuthTokenProvider(OAuthClient oAuthClient) { - this.oAuthClient = oAuthClient; - } - - @Override - public Mono getToken(ConnectionContext connectionContext) { - return Mono.fromSupplier(() -> { - OAuth2AccessTokenWithAdditionalInfo token = oAuthClient.getToken(); - return token.getAuthorizationHeaderValue(); - }); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudApplication.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudApplication.java deleted file mode 100644 index 898cea1a8a..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudApplication.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Collections; -import java.util.Map; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableLifecycle; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle; -import org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType; -import org.cloudfoundry.client.v3.BuildpackData; -import org.cloudfoundry.client.v3.CnbData; -import org.cloudfoundry.client.v3.LifecycleData; -import org.cloudfoundry.client.v3.applications.Application; -import org.cloudfoundry.client.v3.applications.ApplicationState; -import org.immutables.value.Value; - -@Value.Immutable -public abstract class RawCloudApplication extends RawCloudEntity { - - public static final String BUILDPACKS = "buildpacks"; - public static final String STACK = "stack"; - - public abstract Application getApplication(); - - public abstract Derivable getSpace(); - - @Override - public CloudApplication derive() { - Application app = getApplication(); - return ImmutableCloudApplication.builder() - .metadata(parseResourceMetadata(app)) - .v3Metadata(app.getMetadata()) - .name(app.getName()) - .state(parseState(app.getState())) - .lifecycle(parseLifecycle(app.getLifecycle())) - .space(getSpace().derive()) - .build(); - } - - private static CloudApplication.State parseState(ApplicationState state) { - return CloudApplication.State.valueOf(state.getValue()); - } - - private static Lifecycle parseLifecycle(org.cloudfoundry.client.v3.Lifecycle lifecycle) { - Map data = extractLifecycleData(lifecycle.getData()); - - return ImmutableLifecycle.builder() - .type(LifecycleType.valueOf(lifecycle.getType() - .toString() - .toUpperCase())) - .data(data) - .build(); - } - - private static Map extractLifecycleData(LifecycleData lifecycleData) { - if (lifecycleData instanceof BuildpackData buildpackData) { - return Map.of( - BUILDPACKS, buildpackData.getBuildpacks(), - STACK, buildpackData.getStack()); - } else if (lifecycleData instanceof CnbData cnbData) { - return Map.of( - BUILDPACKS, cnbData.getBuildpacks(), - STACK, cnbData.getStack()); - } else { - return Collections.emptyMap(); - } - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudAsyncJob.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudAsyncJob.java deleted file mode 100644 index 1e8bad359c..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudAsyncJob.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.stream.Collectors; - -import org.cloudfoundry.client.v3.jobs.Job; -import org.cloudfoundry.client.v3.jobs.Warning; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; - -@Value.Immutable -public abstract class RawCloudAsyncJob extends RawCloudEntity { - - @Value.Parameter - public abstract Job getJob(); - - @Override - public CloudAsyncJob derive() { - Job job = getJob(); - return ImmutableCloudAsyncJob.builder() - .metadata(parseResourceMetadata(job)) - .state(job.getState()) - .operation(job.getOperation()) - .warnings(getWarnings(job)) - .errors(getErrors(job)) - .build(); - } - - private String getWarnings(Job job) { - return job.getWarnings() - .stream() - .map(Warning::getDetail) - .collect(Collectors.joining(",")); - } - - private String getErrors(Job job) { - return job.getErrors() - .stream() - .map(this::joinErrorDetails) - .collect(Collectors.joining(",")); - } - - private String joinErrorDetails(org.cloudfoundry.client.v3.Error error) { - return String.join(" ", error.getCode() - .toString(), - error.getTitle(), error.getDetail()); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudBuild.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudBuild.java deleted file mode 100644 index 2cdbc39eaa..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudBuild.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Optional; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.builds.Build; -import org.cloudfoundry.client.v3.builds.BuildState; -import org.cloudfoundry.client.v3.builds.CreatedBy; -import org.cloudfoundry.client.v3.builds.Droplet; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild.ImmutableCreatedBy; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild.ImmutablePackageInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDropletInfo; - -@Value.Immutable -public abstract class RawCloudBuild extends RawCloudEntity { - - private static CloudBuild.CreatedBy parseCreatedBy(Build buildResource) { - CreatedBy createdBy = buildResource.getCreatedBy(); - return ImmutableCreatedBy.builder() - .guid(parseNullableGuid(createdBy.getId())) - .name(createdBy.getName()) - .build(); - } - - private static CloudBuild.PackageInfo parsePackageInfo(Build buildResource) { - Relationship packageRelationship = buildResource.getInputPackage(); - String packageId = packageRelationship.getId(); - return ImmutablePackageInfo.of(parseNullableGuid(packageId)); - } - - private static DropletInfo parseDropletInfo(Build buildResource) { - Droplet droplet = buildResource.getDroplet(); - return Optional.ofNullable(droplet) - .map(Droplet::getId) - .map(RawCloudEntity::parseNullableGuid) - .map(dropletGuid -> ImmutableDropletInfo.builder() - .guid(dropletGuid) - .build()) - .orElse(null); - } - - private static CloudBuild.State parseState(BuildState state) { - return parseEnum(state, CloudBuild.State.class); - } - - @Value.Parameter - public abstract Build getResource(); - - @Override - public CloudBuild derive() { - Build resource = getResource(); - return ImmutableCloudBuild.builder() - .metadata(parseResourceMetadata(resource)) - .createdBy(parseCreatedBy(resource)) - .packageInfo(parsePackageInfo(resource)) - .dropletInfo(parseDropletInfo(resource)) - .state(parseState(resource.getState())) - .error(resource.getError()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudDomain.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudDomain.java deleted file mode 100644 index f03981dbaa..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudDomain.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.domains.Domain; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudDomain; - -@Value.Immutable -public abstract class RawCloudDomain extends RawCloudEntity { - - @Value.Parameter - public abstract Domain getResource(); - - @Override - public CloudDomain derive() { - Domain resource = getResource(); - return ImmutableCloudDomain.builder() - .metadata(parseResourceMetadata(resource)) - .name(resource.getName()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEntity.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEntity.java deleted file mode 100644 index 537b1260e2..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEntity.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.text.MessageFormat; -import java.time.LocalDateTime; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.Collection; -import java.util.UUID; -import java.util.stream.Collectors; - -import org.cloudfoundry.client.v3.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; - -public abstract class RawCloudEntity implements Derivable { - - private static final Logger LOGGER = LoggerFactory.getLogger(RawCloudEntity.class); - - protected RawCloudEntity() { - // Recommended by Sonar. - } - - public static CloudMetadata parseResourceMetadata(Resource resource) { - return ImmutableCloudMetadata.builder() - .guid(parseNullableGuid(resource.getId())) - .createdAt(parseNullableDate(resource.getCreatedAt())) - .updatedAt(parseNullableDate(resource.getUpdatedAt())) - .build(); - } - - protected static UUID parseNullableGuid(String guid) { - return guid == null ? null : parseGuid(guid); - } - - protected static UUID parseGuid(String guid) { - try { - return UUID.fromString(guid); - } catch (IllegalArgumentException e) { - LOGGER.warn(MessageFormat.format("Could not parse GUID string: \"{0}\"", guid), e); - return null; - } - } - - protected static LocalDateTime parseNullableDate(String date) { - return date == null ? null : parseDate(date); - } - - protected static LocalDateTime parseDate(String dateString) { - try { - return ZonedDateTime.parse(dateString, DateTimeFormatter.ISO_DATE_TIME) - .toLocalDateTime(); - } catch (DateTimeParseException e) { - LOGGER.warn(MessageFormat.format("Could not parse date string: \"{0}\"", dateString), e); - return null; - } - } - - protected static > E parseEnum(Enum value, Class targetEnum) { - String name = value.name() - .toUpperCase(); - return Enum.valueOf(targetEnum, name); - } - - protected static D deriveFromNullable(Derivable derivable) { - return derivable == null ? null : derivable.derive(); - } - - protected static Collection derive(Collection> derivables) { - return derivables.stream() - .map(Derivable::derive) - .collect(Collectors.toList()); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEvent.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEvent.java deleted file mode 100644 index 7a0937f970..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.auditevents.AuditEventActor; -import org.cloudfoundry.client.v3.auditevents.AuditEventResource; -import org.cloudfoundry.client.v3.auditevents.AuditEventTarget; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent.Participant; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudEvent; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudEvent.ImmutableParticipant; - -@Value.Immutable -public abstract class RawCloudEvent extends RawCloudEntity { - - @Value.Parameter - public abstract AuditEventResource getResource(); - - @Override - public CloudEvent derive() { - AuditEventResource resource = getResource(); - return ImmutableCloudEvent.builder() - .metadata(parseResourceMetadata(resource)) - .target(parseTarget(resource)) - .actor(parseActor(resource)) - .type(resource.getType()) - .build(); - } - - private static Participant parseTarget(AuditEventResource resource) { - AuditEventTarget target = resource.getAuditEventTarget(); - if (target == null) { - return ImmutableParticipant.builder() - .build(); - } - return ImmutableParticipant.builder() - .guid(parseNullableGuid(target.getId())) - .name(target.getName()) - .type(target.getType()) - .build(); - } - - private static Participant parseActor(AuditEventResource resource) { - AuditEventActor actor = resource.getAuditEventActor(); - if (actor == null) { - return ImmutableParticipant.builder() - .build(); - } - return ImmutableParticipant.builder() - .guid(parseNullableGuid(actor.getId())) - .name(actor.getName()) - .type(actor.getType()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudPackage.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudPackage.java deleted file mode 100644 index 73933bf781..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudPackage.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.Checksum; -import org.cloudfoundry.client.v3.packages.BitsData; -import org.cloudfoundry.client.v3.packages.DockerData; -import org.cloudfoundry.client.v3.packages.Package; -import org.cloudfoundry.client.v3.packages.PackageType; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudPackage; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableBitsData; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudPackage; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDockerData; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Status; - -@Value.Immutable -public abstract class RawCloudPackage extends RawCloudEntity { - - @Value.Parameter - public abstract Package getResource(); - - @Override - public CloudPackage derive() { - Package resource = getResource(); - return ImmutableCloudPackage.builder() - .metadata(parseResourceMetadata(resource)) - .status(parseStatus(resource)) - .data(parseData(resource)) - .type(parseType(resource)) - .build(); - } - - private static Status parseStatus(Package resource) { - return parseEnum(resource.getState(), Status.class); - } - - private static CloudPackage.PackageData parseData(Package resource) { - if (resource.getType() == PackageType.BITS) { - return parseBitsData((BitsData) resource.getData()); - } - return parseDockerData((DockerData) resource.getData()); - } - - private static CloudPackage.PackageData parseBitsData(BitsData data) { - return ImmutableBitsData.builder() - .checksum(parseBitsChecksum(data.getChecksum())) - .error(data.getError()) - .build(); - } - - private static org.cloudfoundry.multiapps.controller.client.facade.domain.BitsData.Checksum parseBitsChecksum(Checksum checksum) { - if (checksum == null) { - return null; - } - return ImmutableBitsData.ImmutableChecksum.builder() - .algorithm(checksum.getType() - .toString()) - .value(checksum.getValue()) - .build(); - } - - private static CloudPackage.PackageData parseDockerData(DockerData data) { - return ImmutableDockerData.builder() - .image(data.getImage()) - .username(data.getUsername()) - .password(data.getPassword()) - .build(); - } - - private static CloudPackage.Type parseType(Package resource) { - return parseEnum(resource.getType(), CloudPackage.Type.class); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java deleted file mode 100644 index 931f528b75..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.processes.Data; -import org.cloudfoundry.client.v3.processes.HealthCheck; -import org.cloudfoundry.client.v3.processes.Process; -import org.cloudfoundry.client.v3.processes.ReadinessHealthCheck; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; -import org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudProcess; -import org.immutables.value.Value; - -@Value.Immutable -public abstract class RawCloudProcess extends RawCloudEntity { - - @Value.Parameter - public abstract Process getProcess(); - - @Override - public CloudProcess derive() { - Process process = getProcess(); - HealthCheck healthCheck = process.getHealthCheck(); - ReadinessHealthCheck readinessHealthCheckType = process.getReadinessHealthCheck(); - Integer healthCheckTimeout = null; - String healthCheckHttpEndpoint = null; - Integer healthCheckInvocationTimeout = null; - Integer healthCheckInterval = null; - if (healthCheck.getData() != null) { - Data healthCheckData = healthCheck.getData(); - healthCheckTimeout = healthCheckData.getTimeout(); - healthCheckInvocationTimeout = healthCheckData.getInvocationTimeout(); - healthCheckHttpEndpoint = healthCheckData.getEndpoint(); - healthCheckInterval = healthCheckData.getInterval(); - } - Integer readinessHealthCheckInvocationTimeout = null; - String readinessHealthCheckHttpEndpoint = null; - Integer readinessHealthCheckInterval = null; - if (readinessHealthCheckType.getData() != null) { - Data readinessHealthCheckData = readinessHealthCheckType.getData(); - readinessHealthCheckInvocationTimeout = readinessHealthCheckData.getInvocationTimeout(); - readinessHealthCheckHttpEndpoint = readinessHealthCheckData.getEndpoint(); - readinessHealthCheckInterval = readinessHealthCheckData.getInterval(); - } - return ImmutableCloudProcess.builder() - .command(process.getCommand()) - .instances(process.getInstances()) - .memoryInMb(process.getMemoryInMb()) - .diskInMb(process.getDiskInMb()) - .healthCheckType(HealthCheckType.valueOf(healthCheck.getType() - .getValue() - .toUpperCase())) - .healthCheckHttpEndpoint(healthCheckHttpEndpoint) - .healthCheckTimeout(healthCheckTimeout) - .healthCheckInvocationTimeout(healthCheckInvocationTimeout) - .healthCheckInterval(healthCheckInterval) - .readinessHealthCheckType(readinessHealthCheckType.getType() - .getValue()) - .readinessHealthCheckHttpEndpoint(readinessHealthCheckHttpEndpoint) - .readinessHealthCheckInvocationTimeout(readinessHealthCheckInvocationTimeout) - .readinessHealthCheckInterval(readinessHealthCheckInterval) - .build(); - } -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBinding.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBinding.java deleted file mode 100644 index ab9ba4cad8..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBinding.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.UUID; - -import org.cloudfoundry.client.v3.servicebindings.ServiceBinding; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBinding; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; - -@Value.Immutable -public abstract class RawCloudServiceBinding extends RawCloudEntity { - - @Value.Parameter - public abstract ServiceBindingResource getServiceBinding(); - - @Override - public CloudServiceBinding derive() { - ServiceBinding serviceBinding = getServiceBinding(); - var appRelationship = serviceBinding.getRelationships() - .getApplication(); - return ImmutableCloudServiceBinding.builder() - .metadata(parseResourceMetadata(serviceBinding)) - .applicationGuid(parseNullableGuid(appRelationship == null ? null - : appRelationship.getData() - .getId())) - .serviceInstanceGuid(UUID.fromString(serviceBinding.getRelationships() - .getServiceInstance() - .getData() - .getId())) - .serviceBindingOperation(ServiceCredentialBindingOperation.from(getServiceBinding().getLastOperation())) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBroker.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBroker.java deleted file mode 100644 index d76fcc5cdd..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBroker.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Optional; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.servicebrokers.ServiceBroker; -import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerRelationships; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBroker; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBroker; - -@Value.Immutable -public abstract class RawCloudServiceBroker extends RawCloudEntity { - - @Value.Parameter - public abstract ServiceBroker getServiceBroker(); - - @Override - public CloudServiceBroker derive() { - ServiceBroker serviceBroker = getServiceBroker(); - String spaceGuid = getSpaceGuid(serviceBroker); - return ImmutableCloudServiceBroker.builder() - .metadata(parseResourceMetadata(serviceBroker)) - .name(serviceBroker.getName()) - .url(serviceBroker.getUrl()) - .spaceGuid(spaceGuid) - .build(); - } - - private String getSpaceGuid(ServiceBroker serviceBroker) { - return Optional.ofNullable(serviceBroker.getRelationships()) - .map(ServiceBrokerRelationships::getSpace) - .map(ToOneRelationship::getData) - .map(Relationship::getId) - .orElse(null); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceInstance.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceInstance.java deleted file mode 100644 index af4d58b936..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceInstance.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Optional; - -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceResource; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOffering; -import org.cloudfoundry.client.v3.serviceplans.ServicePlan; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.Nullable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation; - -@Value.Immutable -public abstract class RawCloudServiceInstance extends RawCloudEntity { - - @Value.Parameter - public abstract ServiceInstanceResource getResource(); - - @Nullable - public abstract ServicePlan getServicePlan(); - - @Nullable - public abstract ServiceOffering getServiceOffering(); - - @Override - public CloudServiceInstance derive() { - ServiceInstanceResource resource = getResource(); - return ImmutableCloudServiceInstance.builder() - .metadata(parseResourceMetadata(resource)) - .v3Metadata(resource.getMetadata()) - .name(resource.getName()) - .plan(getServicePlanName()) - .label(getLabelName()) - .type(resource.getType()) - .tags(resource.getTags()) - .lastOperation(ServiceOperation.fromLastOperation(resource.getLastOperation())) - .syslogDrainUrl(resource.getSyslogDrainUrl()) - .build(); - } - - private String getServicePlanName() { - return Optional.ofNullable(getServicePlan()) - .map(ServicePlan::getName) - .orElse(null); - } - - private String getLabelName() { - return Optional.ofNullable(getServiceOffering()) - .map(ServiceOffering::getName) - .orElse(null); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceKey.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceKey.java deleted file mode 100644 index 97f6e8eb31..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceKey.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Map; - -import org.cloudfoundry.AllowNulls; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.Nullable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; - -@Value.Immutable -public abstract class RawCloudServiceKey extends RawCloudEntity { - - public abstract ServiceBindingResource getServiceBindingResource(); - - @Nullable - @AllowNulls - public abstract Map getCredentials(); - - public abstract Derivable getServiceInstance(); - - @Override - public CloudServiceKey derive() { - ServiceBindingResource serviceBindingResource = getServiceBindingResource(); - Derivable serviceInstance = getServiceInstance(); - return ImmutableCloudServiceKey.builder() - .metadata(parseResourceMetadata(serviceBindingResource)) - .v3Metadata(serviceBindingResource.getMetadata()) - .name(serviceBindingResource.getName()) - .credentials(getCredentials()) - .serviceInstance(serviceInstance.derive()) - .serviceKeyOperation(ServiceCredentialBindingOperation.from(serviceBindingResource.getLastOperation())) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceOffering.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceOffering.java deleted file mode 100644 index c252aa50bd..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceOffering.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.List; - -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingResource; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceOffering; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceOffering; - -@Value.Immutable -public abstract class RawCloudServiceOffering extends RawCloudEntity { - - public abstract ServiceOfferingResource getServiceOffering(); - - public abstract List> getServicePlans(); - - @Override - public CloudServiceOffering derive() { - ServiceOfferingResource serviceOffering = getServiceOffering(); - return ImmutableCloudServiceOffering.builder() - .metadata(parseResourceMetadata(serviceOffering)) - .name(serviceOffering.getName()) - .isAvailable(serviceOffering.getAvailable()) - .isBindable(serviceOffering.getBrokerCatalog() - .getFeatures() - .getBindable()) - .description(serviceOffering.getDescription()) - .isShareable(serviceOffering.getShareable()) - .extra(serviceOffering.getBrokerCatalog() - .getMetadata()) - .docUrl(serviceOffering.getDocumentationUrl()) - .brokerId(serviceOffering.getRelationships() - .getServiceBroker() - .getData() - .getId()) - .uniqueId(serviceOffering.getBrokerCatalog() - .getBrokerCatalogId()) - .servicePlans(derive(getServicePlans())) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServicePlan.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServicePlan.java deleted file mode 100644 index a79527037e..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServicePlan.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.serviceplans.ServicePlan; -import org.cloudfoundry.client.v3.serviceplans.Visibility; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServicePlan; - -@Value.Immutable -public abstract class RawCloudServicePlan extends RawCloudEntity { - - @Value.Parameter - public abstract ServicePlan getResource(); - - @Override - public CloudServicePlan derive() { - ServicePlan resource = getResource(); - return ImmutableCloudServicePlan.builder() - .metadata(parseResourceMetadata(resource)) - .name(resource.getName()) - .description(resource.getDescription()) - .extra(resource.getBrokerCatalog() - .getMetadata()) - .uniqueId(resource.getBrokerCatalog() - .getBrokerCatalogId()) - .serviceOfferingId(resource.getRelationships() - .getServiceOffering() - .getData() - .getId()) - .isPublic(resource.getVisibilityType() - .equals(Visibility.PUBLIC)) - .isFree(resource.getFree()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudStack.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudStack.java deleted file mode 100644 index 6c2a8ce152..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudStack.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.stacks.Stack; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudStack; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudStack; - -@Value.Immutable -public abstract class RawCloudStack extends RawCloudEntity { - - @Value.Parameter - public abstract Stack getStack(); - - @Override - public CloudStack derive() { - Stack stack = getStack(); - return ImmutableCloudStack.builder() - .metadata(parseResourceMetadata(stack)) - .name(stack.getName()) - .description(stack.getDescription()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudTask.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudTask.java deleted file mode 100644 index b963d4457a..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudTask.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Optional; - -import org.cloudfoundry.client.v3.tasks.Result; -import org.cloudfoundry.client.v3.tasks.Task; -import org.cloudfoundry.client.v3.tasks.TaskState; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudTask; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudTask; - -@Value.Immutable -public abstract class RawCloudTask extends RawCloudEntity { - - @Value.Parameter - public abstract Task getResource(); - - @Override - public CloudTask derive() { - Task resource = getResource(); - return ImmutableCloudTask.builder() - .metadata(parseResourceMetadata(resource)) - .name(resource.getName()) - .command(resource.getCommand()) - .limits(parseLimits(resource)) - .result(parseResult(resource)) - .state(parseState(resource.getState())) - .build(); - } - - private static CloudTask.Result parseResult(Task resource) { - return Optional.ofNullable(resource.getResult()) - .map(Result::getFailureReason) - .map(ImmutableCloudTask.ImmutableResult::of) - .orElse(null); - } - - private static CloudTask.Limits parseLimits(Task resource) { - return ImmutableCloudTask.ImmutableLimits.builder() - .disk(resource.getDiskInMb()) - .memory(resource.getMemoryInMb()) - .build(); - } - - private static CloudTask.State parseState(TaskState state) { - return parseEnum(state, CloudTask.State.class); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawUserRole.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawUserRole.java deleted file mode 100644 index 8eacc03f55..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawUserRole.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.client.v3.roles.RoleResource; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.UserRole; - -@Value.Immutable -public abstract class RawUserRole implements Derivable { - - @Value.Parameter - public abstract RoleResource getRoleResource(); - - @Override - public UserRole derive() { - RoleResource role = getRoleResource(); - return UserRole.fromRoleType(role.getType()); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawV3CloudServiceInstance.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawV3CloudServiceInstance.java deleted file mode 100644 index 8155a4ad41..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawV3CloudServiceInstance.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstance; -import org.immutables.value.Value; - -@Value.Immutable -public abstract class RawV3CloudServiceInstance extends RawCloudEntity { - - @Value.Parameter - public abstract ServiceInstance getServiceInstance(); - - @Override - public CloudServiceInstance derive() { - ServiceInstance serviceInstance = getServiceInstance(); - return ImmutableCloudServiceInstance.builder() - .metadata(parseResourceMetadata(serviceInstance)) - .v3Metadata(serviceInstance.getMetadata()) - .name(serviceInstance.getName()) - .tags(serviceInstance.getTags()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudAsyncJob.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudAsyncJob.java index 394a40c3b7..20b52febc8 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudAsyncJob.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudAsyncJob.java @@ -1,6 +1,5 @@ package org.cloudfoundry.multiapps.controller.client.facade.domain; -import org.cloudfoundry.client.v3.jobs.JobState; import org.immutables.value.Value; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudEntity.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudEntity.java index 16bf0aef9b..aef922d829 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudEntity.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudEntity.java @@ -2,7 +2,7 @@ import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.Nullable; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceBinding.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceBinding.java index bceb94223b..e3fb8544fe 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceBinding.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceBinding.java @@ -2,7 +2,7 @@ import java.util.UUID; -import org.cloudfoundry.Nullable; +import org.cloudfoundry.multiapps.controller.client.facade.Nullable; import org.immutables.value.Value; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceInstance.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceInstance.java index 78db202828..2cf5216efa 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceInstance.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceInstance.java @@ -3,8 +3,7 @@ import java.util.List; import java.util.Map; -import org.cloudfoundry.AllowNulls; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.AllowNulls; import org.immutables.value.Value; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceKey.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceKey.java index a77cad1967..76c211eeda 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceKey.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudServiceKey.java @@ -2,7 +2,7 @@ import java.util.Map; -import org.cloudfoundry.AllowNulls; +import org.cloudfoundry.multiapps.controller.client.facade.AllowNulls; import org.immutables.value.Value; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/HealthCheckType.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/HealthCheckType.java index 6cd1a86842..992fa758a3 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/HealthCheckType.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/HealthCheckType.java @@ -3,8 +3,13 @@ public enum HealthCheckType { HTTP, PORT, PROCESS, @Deprecated NONE; + public String getValue() { + return name().toLowerCase(); + } + @Override public String toString() { - return this.name().toLowerCase(); + return this.name() + .toLowerCase(); } } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/InstanceState.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/InstanceState.java index 89d88d803d..a5425130ed 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/InstanceState.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/InstanceState.java @@ -1,16 +1,14 @@ package org.cloudfoundry.multiapps.controller.client.facade.domain; -import org.cloudfoundry.client.v3.processes.ProcessState; - public enum InstanceState { CRASHED, DOWN, RUNNING, STARTING, UNKNOWN; - - public static InstanceState valueOfWithDefault(ProcessState state) { + + public static InstanceState valueOfWithDefault(String state) { if (state == null) { return UNKNOWN; } try { - return InstanceState.valueOf(state.getValue()); + return InstanceState.valueOf(state); } catch (IllegalArgumentException e) { return InstanceState.UNKNOWN; } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/JobState.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/JobState.java new file mode 100644 index 0000000000..9c3445f45a --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/JobState.java @@ -0,0 +1,34 @@ +package org.cloudfoundry.multiapps.controller.client.facade.domain; + +import java.text.MessageFormat; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import org.cloudfoundry.multiapps.controller.Messages; + +public enum JobState { + + PROCESSING("processing"), POLLING("polling"), COMPLETE("complete"), FAILED("failed"); + + private final String value; + + JobState(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static JobState from(String value) { + for (JobState state : values()) { + if (state.value.equals(value)) { + return state; + } + } + + throw new IllegalArgumentException(MessageFormat.format(Messages.UNKNOWN_JOB_STATE_0, value)); + } +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/LastOperation.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/LastOperation.java new file mode 100644 index 0000000000..0e43b64c12 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/LastOperation.java @@ -0,0 +1,4 @@ +package org.cloudfoundry.multiapps.controller.client.facade.domain; + +public record LastOperation(String type, String state, String description, String createdAt, String updatedAt) { +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Lifecycle.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Lifecycle.java index f448f2c668..d6453dd89e 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Lifecycle.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Lifecycle.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.cloudfoundry.multiapps.controller.client.facade.Nullable; -import org.cloudfoundry.AllowNulls; +import org.cloudfoundry.multiapps.controller.client.facade.AllowNulls; import org.immutables.value.Value; import java.util.Map; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Metadata.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Metadata.java new file mode 100644 index 0000000000..342e1d2d44 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/Metadata.java @@ -0,0 +1,115 @@ +package org.cloudfoundry.multiapps.controller.client.facade.domain; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public final class Metadata { + + private final Map labels; + private final Map annotations; + + @JsonCreator + Metadata(@JsonProperty("labels") Map labels, @JsonProperty("annotations") Map annotations) { + this.labels = labels == null ? Collections.emptyMap() : Collections.unmodifiableMap(new LinkedHashMap<>(labels)); + this.annotations = annotations == null ? Collections.emptyMap() : Collections.unmodifiableMap(new LinkedHashMap<>(annotations)); + } + + @JsonProperty("labels") + public Map getLabels() { + return labels; + } + + @JsonProperty("annotations") + public Map getAnnotations() { + return annotations; + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + Metadata metadata = (Metadata) o; + return labels.equals(metadata.labels) && annotations.equals(metadata.annotations); + } + + @Override + public int hashCode() { + return Objects.hash(labels, annotations); + } + + @Override + public String toString() { + return "Metadata{annotations=" + annotations + ", labels=" + labels + "}"; + } + + public static final class Builder { + + private final Map labels = new LinkedHashMap<>(); + private final Map annotations = new LinkedHashMap<>(); + + private Builder() { + } + + public Builder from(Metadata metadata) { + if (metadata != null) { + this.labels.putAll(metadata.getLabels()); + this.annotations.putAll(metadata.getAnnotations()); + } + + return this; + } + + public Builder label(String key, String value) { + this.labels.put(key, value); + return this; + } + + public Builder labels(Map labels) { + if (labels != null) { + this.labels.putAll(labels); + } + + return this; + } + + public Builder putAllLabels(Map labels) { + return labels(labels); + } + + public Builder annotation(String key, String value) { + this.annotations.put(key, value); + return this; + } + + public Builder annotations(Map annotations) { + if (annotations != null) { + this.annotations.putAll(annotations); + } + + return this; + } + + public Builder putAllAnnotations(Map annotations) { + return annotations(annotations); + } + + public Metadata build() { + return new Metadata(labels, annotations); + } + } +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceCredentialBindingOperation.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceCredentialBindingOperation.java index 4f202834cc..73403202f1 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceCredentialBindingOperation.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceCredentialBindingOperation.java @@ -5,12 +5,10 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; -import org.cloudfoundry.client.v3.LastOperation; -import org.immutables.value.Value; - import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.cloudfoundry.multiapps.controller.client.facade.Nullable; +import org.immutables.value.Value; @Value.Immutable @JsonSerialize(as = ImmutableServiceCredentialBindingOperation.class) @@ -31,11 +29,12 @@ public abstract class ServiceCredentialBindingOperation { public abstract LocalDateTime getUpdatedAt(); public static ServiceCredentialBindingOperation from(LastOperation lastOperation) { - String lastOperationType = lastOperation.getType(); - String lastOperationState = lastOperation.getState(); - String lastOperationDescription = lastOperation.getDescription(); - String lastOperationCreatedAt = lastOperation.getCreatedAt(); - String lastOperationUpdatedAt = lastOperation.getUpdatedAt(); + String lastOperationType = lastOperation.type(); + String lastOperationState = lastOperation.state(); + String lastOperationDescription = lastOperation.description(); + String lastOperationCreatedAt = lastOperation.createdAt(); + String lastOperationUpdatedAt = lastOperation.updatedAt(); + return ImmutableServiceCredentialBindingOperation.builder() .type(ServiceCredentialBindingOperation.Type.fromString(lastOperationType)) .state(ServiceCredentialBindingOperation.State.fromString(lastOperationState)) @@ -55,8 +54,9 @@ public static Type fromString(String value) { .filter(type -> type.toString() .equals(value)) .findFirst() - .orElseThrow(() -> new IllegalArgumentException(MessageFormat.format("Illegal service binding operation type: \"{0}\"", - value))); + .orElseThrow( + () -> new IllegalArgumentException(MessageFormat.format("Illegal service binding operation type: \"{0}\"", + value))); } @Override diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceInstanceType.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceInstanceType.java new file mode 100644 index 0000000000..434443b306 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceInstanceType.java @@ -0,0 +1,34 @@ +package org.cloudfoundry.multiapps.controller.client.facade.domain; + +import java.text.MessageFormat; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import org.cloudfoundry.multiapps.controller.Messages; + +public enum ServiceInstanceType { + + MANAGED("managed"), USER_PROVIDED("user-provided"); + + private final String value; + + ServiceInstanceType(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @JsonCreator + public static ServiceInstanceType from(String value) { + for (ServiceInstanceType type : values()) { + if (type.value.equals(value)) { + return type; + } + } + + throw new IllegalArgumentException(MessageFormat.format(Messages.UNKNOWN_SERVICE_INSTANCE_TYPE_0, value)); + } +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceOperation.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceOperation.java index 151dae6bfa..7edff81be7 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceOperation.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/ServiceOperation.java @@ -3,8 +3,6 @@ import java.text.MessageFormat; import java.util.Objects; -import org.cloudfoundry.client.v3.LastOperation; - public class ServiceOperation { public enum Type { @@ -82,13 +80,14 @@ public State getState() { } public static ServiceOperation fromLastOperation(LastOperation lastOperation) { - if (lastOperation == null || lastOperation.getType() == null || lastOperation.getState() == null) { + if (lastOperation == null || lastOperation.type() == null || lastOperation.state() == null) { return null; } - Type type = Type.fromString(lastOperation.getType()); - State state = State.fromString(lastOperation.getState()); - String description = lastOperation.getDescription(); - return new ServiceOperation(type, description, state); + + Type operationType = Type.fromString(lastOperation.type()); + State operationState = State.fromString(lastOperation.state()); + String operationDescription = lastOperation.description(); + return new ServiceOperation(operationType, operationDescription, operationState); } @Override diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/UserRole.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/UserRole.java index 3ee5e84386..8644a5cdf2 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/UserRole.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/UserRole.java @@ -1,10 +1,11 @@ package org.cloudfoundry.multiapps.controller.client.facade.domain; +import java.text.MessageFormat; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.roles.RoleType; +import org.cloudfoundry.multiapps.controller.Messages; public enum UserRole { @@ -18,13 +19,15 @@ public enum UserRole { private static final Map NAMES_TO_VALUES = Arrays.stream(values()) .collect(Collectors.toMap(UserRole::getName, - roleType -> roleType)); + roleType -> roleType)); + + public static UserRole fromRoleType(String roleTypeValue) { + UserRole userRole = NAMES_TO_VALUES.get(roleTypeValue); - public static UserRole fromRoleType(RoleType roleType) { - UserRole userRole = NAMES_TO_VALUES.get(roleType.getValue()); if (userRole == null) { - throw new IllegalArgumentException("Unknown user role: " + roleType.getValue()); + throw new IllegalArgumentException(MessageFormat.format(Messages.UNKNOWN_USER_ROLE_0, roleTypeValue)); } + return userRole; } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/dto/ApplicationToCreateDto.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/dto/ApplicationToCreateDto.java index e23244f2f4..c32aa17139 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/dto/ApplicationToCreateDto.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/dto/ApplicationToCreateDto.java @@ -3,7 +3,7 @@ import java.util.Map; import java.util.Set; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.immutables.value.Value; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/oauth2/OAuthClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/oauth2/OAuthClient.java index 414fc822b8..03ae12365f 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/oauth2/OAuthClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/oauth2/OAuthClient.java @@ -8,9 +8,7 @@ import org.cloudfoundry.multiapps.controller.client.facade.CloudCredentials; import org.cloudfoundry.multiapps.controller.client.facade.Constants; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.OAuthTokenProvider; import org.cloudfoundry.multiapps.controller.client.facade.util.JsonUtil; -import org.cloudfoundry.reactor.TokenProvider; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; @@ -78,10 +76,6 @@ public String getAuthorizationHeaderValue() { return null; } - public TokenProvider getTokenProvider() { - return new OAuthTokenProvider(this); - } - private boolean shouldRefreshToken() { return credentials.isRefreshable() && token.getOAuth2AccessToken() .getExpiresAt() diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ApplicationsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ApplicationsV3Operations.java new file mode 100644 index 0000000000..12a19a3b8b --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ApplicationsV3Operations.java @@ -0,0 +1,268 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; +import org.cloudfoundry.multiapps.controller.client.facade.dto.ApplicationToCreateDto; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Application; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ApplicationMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; + +public class ApplicationsV3Operations { + + private static final ParameterizedTypeReference> APPLICATION_PAGE = new ParameterizedTypeReference<>() { + }; + + private static final Duration DELETE_JOB_TIMEOUT = Duration.ofMinutes(5); + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ApplicationsV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public UUID createApplication(ApplicationToCreateDto dto) { + if (target == null || target.getGuid() == null) { + throw new CloudOperationException(HttpStatus.BAD_REQUEST, Messages.BAD_REQUEST, + Messages.TARGET_SPACE_REQUIRED_TO_CREATE_AN_APPLICATION); + } + Map body = new HashMap<>(); + body.put("name", dto.getName()); + body.put("lifecycle", buildLifecycle(dto.getStaging())); + body.put("relationships", Map.of("space", Map.of("data", Map.of("guid", target.getGuid() + .toString())))); + if (dto.getEnv() != null) { + body.put("environment_variables", dto.getEnv()); + } + if (dto.getMetadata() != null) { + body.put("metadata", Map.of("labels", dto.getMetadata() + .getLabels(), + "annotations", dto.getMetadata() + .getAnnotations())); + } + V3Application created = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.APPS) + .body(body) + .retrieve() + .body(V3Application.class); + UUID appGuid = UUID.fromString(created.guid()); + Map scale = new HashMap<>(); + if (dto.getMemoryInMb() != null) { + scale.put("memory_in_mb", dto.getMemoryInMb()); + } + if (dto.getDiskQuotaInMb() != null) { + scale.put("disk_in_mb", dto.getDiskQuotaInMb()); + } + if (!scale.isEmpty()) { + scaleWebProcess(appGuid, scale); + } + return appGuid; + } + + private Map buildLifecycle(Staging staging) { + if (staging == null) { + return Map.of("type", "buildpack", "data", Map.of()); + } + if (staging.getDockerInfo() != null) { + return Map.of("type", "docker", "data", Map.of()); + } + String type = staging.getLifecycleType() != null ? staging.getLifecycleType() + .name() + .toLowerCase() + : "buildpack"; + Map data = new HashMap<>(); + if (staging.getBuildpacks() != null) { + data.put("buildpacks", staging.getBuildpacks()); + } + if (staging.getStackName() != null) { + data.put("stack", staging.getStackName()); + } + return Map.of("type", type, "data", data); + } + + public void deleteApplication(String applicationName) { + UUID applicationGuid = getApplicationGuid(applicationName); + var response = cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.APP_BY_GUID, applicationGuid) + .retrieve() + .toBodilessEntity(); + cc.followAsyncJob(response, DELETE_JOB_TIMEOUT); + } + + public CloudApplication getApplication(String applicationName) { + return getApplication(applicationName, true); + } + + public CloudApplication getApplication(String applicationName, boolean required) { + V3Application app = findApplicationByName(applicationName); + if (app == null) { + if (required) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + return null; + } + return V3ApplicationMapper.toCloudApplication(app, target); + } + + public UUID getApplicationGuid(String applicationName) { + return getApplication(applicationName).getGuid(); + } + + public String getApplicationName(UUID applicationGuid) { + V3Application app = cc.get(CloudControllerV3Endpoints.APPS + "/" + applicationGuid, V3Application.class); + return app == null ? null : app.name(); + } + + public Map getApplicationEnvironment(UUID applicationGuid) { + V3Application.V3EnvironmentVariables env = cc.get(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + + "/environment_variables", + V3Application.V3EnvironmentVariables.class); + return env == null || env.var() == null ? Map.of() : env.var(); + } + + public Map getApplicationEnvironment(String applicationName) { + return getApplicationEnvironment(getApplicationGuid(applicationName)); + } + + public List getApplications() { + return listApplications(applicationsQuery(null)).stream() + .map(app -> V3ApplicationMapper.toCloudApplication(app, target)) + .toList(); + } + + public List getApplicationsByMetadataLabelSelector(String labelSelector) { + String query = applicationsQuery(null); + if (labelSelector != null) { + query = query + CloudControllerV3Endpoints.AMPERSAND_LABEL_SELECTOR + labelSelector; + } + return listApplications(query).stream() + .map(app -> V3ApplicationMapper.toCloudApplication(app, target)) + .toList(); + } + + public void startApplication(String applicationName) { + UUID guid = getApplicationGuid(applicationName); + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.APP_START, guid) + .retrieve() + .toBodilessEntity(); + } + + public void stopApplication(String applicationName) { + UUID guid = getApplicationGuid(applicationName); + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.APP_STOP, guid) + .retrieve() + .toBodilessEntity(); + } + + public void rename(String applicationName, String newName) { + UUID guid = getApplicationGuid(applicationName); + try { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.APP_BY_GUID, guid) + .body(Map.of("name", newName)) + .retrieve() + .toBodilessEntity(); + } catch (CloudOperationException e) { + //the Cloud Controller can return 503 but the rename might have happened already + if (e.getStatusCode() == HttpStatus.SERVICE_UNAVAILABLE && newName.equals(getApplicationName(guid))) { + return; + } + throw e; + } + } + + public void updateApplicationInstances(String applicationName, int instances) { + scaleWebProcess(getApplicationGuid(applicationName), Map.of("instances", instances)); + } + + public void updateApplicationMemory(String applicationName, int memory) { + scaleWebProcess(getApplicationGuid(applicationName), Map.of("memory_in_mb", memory)); + } + + public void updateApplicationDiskQuota(String applicationName, int disk) { + scaleWebProcess(getApplicationGuid(applicationName), Map.of("disk_in_mb", disk)); + } + + public void updateApplicationEnv(String applicationName, Map env) { + UUID guid = getApplicationGuid(applicationName); + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.APP_ENV_VARS, guid) + .body(Map.of("var", env)) + .retrieve() + .toBodilessEntity(); + } + + public void bindDropletToApp(UUID dropletGuid, UUID applicationGuid) { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.APP_CURRENT_DROPLET, applicationGuid) + .body(Map.of("data", Map.of("guid", dropletGuid.toString()))) + .retrieve() + .toBodilessEntity(); + } + + public void updateApplicationMetadata(UUID guid, Metadata metadata) { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.APP_BY_GUID, guid) + .body(Map.of("metadata", Map.of("labels", metadata.getLabels(), "annotations", metadata.getAnnotations()))) + .retrieve() + .toBodilessEntity(); + } + + private void scaleWebProcess(UUID applicationGuid, Map scaleBody) { + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.APP_WEB_PROCESS_SCALE, applicationGuid) + .body(scaleBody) + .retrieve() + .toBodilessEntity(); + } + + private V3Application findApplicationByName(String applicationName) { + List apps = listApplications(applicationsQuery(applicationName)); + return apps.isEmpty() ? null : apps.get(0); + } + + private List listApplications(String query) { + return cc.list(query, APPLICATION_PAGE); + } + + private String applicationsQuery(String name) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + if (name != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(name); + } + return query.toString(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/BuildsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/BuildsV3Operations.java new file mode 100644 index 0000000000..c9416c6bd0 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/BuildsV3Operations.java @@ -0,0 +1,56 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Build; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3BuildMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.springframework.core.ParameterizedTypeReference; + +public class BuildsV3Operations { + + private final CloudControllerV3Client cc; + + public BuildsV3Operations(CloudControllerV3Client cc) { + this.cc = cc; + } + + public CloudBuild createBuild(UUID packageGuid) { + V3Build build = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.BUILDS) + .body(Map.of("package", Map.of("guid", packageGuid.toString()))) + .retrieve() + .body(V3Build.class); + return V3BuildMapper.toCloudBuild(build); + } + + public CloudBuild getBuild(UUID buildGuid) { + V3Build build = cc.get(CloudControllerV3Endpoints.BUILDS + "/" + buildGuid, V3Build.class); + return V3BuildMapper.toCloudBuild(build); + } + + public List getBuildsForApplication(UUID applicationGuid) { + String uri = CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/builds" + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + return cc.list(uri, new ParameterizedTypeReference>() { + }) + .stream() + .map(V3BuildMapper::toCloudBuild) + .toList(); + } + + public List getBuildsForPackage(UUID packageGuid) { + String uri = CloudControllerV3Endpoints.BUILDS + CloudControllerV3Endpoints.QUERY_PACKAGE_GUIDS + packageGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + return cc.list(uri, new ParameterizedTypeReference>() { + }) + .stream() + .map(V3BuildMapper::toCloudBuild) + .toList(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ClientConfigurationOptions.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ClientConfigurationOptions.java new file mode 100644 index 0000000000..5e9ef98e2d --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ClientConfigurationOptions.java @@ -0,0 +1,9 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.time.Duration; +import java.util.Optional; + +public record ClientConfigurationOptions(Optional connectionTimeout, Optional responseTimeout, + Optional sslHandshakeTimeout, Optional connectionPoolSize, + Optional threadPoolSize, boolean trustSelfSignedCertificates) { +} \ No newline at end of file diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java index 8c79aec33e..4ed03f71d4 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java @@ -8,7 +8,7 @@ import java.util.Set; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.UploadStatusCallback; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientBuilder.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientBuilder.java new file mode 100644 index 0000000000..754b658584 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientBuilder.java @@ -0,0 +1,181 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.net.URL; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import javax.net.ssl.SSLException; +import javax.net.ssl.X509TrustManager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.channel.ChannelOption; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; +import org.springframework.http.HttpHeaders; +import org.springframework.http.client.ReactorClientHttpRequestFactory; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.RestClient; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; +import reactor.netty.resources.ConnectionProvider; +import reactor.netty.resources.LoopResources; +import reactor.netty.tcp.SslProvider; + +public final class CloudControllerRestClientBuilder { + + private CloudControllerRestClientBuilder() { + } + + public static RestClient build(URL v3ApiUrl, OAuthClient oAuthClient, Map requestTags, + ClientConfigurationOptions configOptions) { + return buildRestClient(buildHttpClient(configOptions), v3ApiUrl, oAuthClient, requestTags); + } + + public static RestClient buildRestClient(HttpClient httpClient, URL baseUrl, OAuthClient oAuthClient, + Map requestTags) { + return RestClient.builder() + .baseUrl(baseUrl.toString()) + .requestFactory(new ReactorClientHttpRequestFactory(httpClient)) + .messageConverters(List.of(new ByteArrayHttpMessageConverter(), + new StringHttpMessageConverter(), + new FormHttpMessageConverter(), + new MappingJackson2HttpMessageConverter(new ObjectMapper()))) + .requestInterceptor((request, body, execution) -> { + String authorization = oAuthClient.getAuthorizationHeaderValue(); + + if (authorization != null) { + request.getHeaders() + .set(HttpHeaders.AUTHORIZATION, authorization); + } + + requestTags.forEach((String key, String value) -> request.getHeaders() + .set(key, value)); + + return execution.execute(request, body); + }) + .defaultStatusHandler(new CloudControllerResponseErrorHandler()) + .build(); + } + + public static WebClient buildWebClient(HttpClient httpClient, URL baseUrl, OAuthClient oAuthClient, + Map requestTags) { + return WebClient.builder() + .baseUrl(baseUrl.toString()) + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .filter(authAndTagsFilter(oAuthClient, requestTags)) + .build(); + } + + private static ExchangeFilterFunction authAndTagsFilter(OAuthClient oAuthClient, Map requestTags) { + return (request, next) -> { + ClientRequest.Builder requestBuilder = ClientRequest.from(request); + String authorizationValue = oAuthClient.getAuthorizationHeaderValue(); + + if (authorizationValue != null) { + requestBuilder.header(HttpHeaders.AUTHORIZATION, authorizationValue); + } + + requestTags.forEach((String key, String value) -> { + requestBuilder.header(key, value); + }); + + return next.exchange(requestBuilder.build()); + }; + } + + static HttpClient buildHttpClient(ClientConfigurationOptions configOptions) { + int poolSize = configOptions.connectionPoolSize() + .orElse(Constants.DEFAULT_CONNECTION_POOL_SIZE); + ConnectionProvider connectionProvider = ConnectionProvider.builder(Constants.CONNECTION_POOL_NAME) + .maxConnections(poolSize) + .build(); + HttpClient httpClient = HttpClient.create(connectionProvider) + .followRedirect(true) + .proxyWithSystemProperties(); + int connectTimeoutMillis = (int) configOptions.connectionTimeout() + .orElse(Constants.DEFAULT_CONNECT_TIMEOUT) + .toMillis(); + httpClient = httpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis); + + Optional threadPoolSize = configOptions.threadPoolSize(); + if (threadPoolSize.isPresent()) { + LoopResources loopResources = LoopResources.create(Constants.CONNECTION_POOL_NAME + Constants.LOOP_RESOURCES_SUFFIX, + threadPoolSize.get(), true); + httpClient = httpClient.runOn(loopResources); + } + + Optional responseTimeout = configOptions.responseTimeout(); + if (responseTimeout.isPresent()) { + httpClient = httpClient.responseTimeout(responseTimeout.get()); + } + + Optional handshakeTimeout = configOptions.sslHandshakeTimeout(); + if (configOptions.trustSelfSignedCertificates()) { + httpClient = httpClient.secure(sslSpecification -> { + SslProvider.Builder sslBuilder = sslSpecification.sslContext(buildTrustAllSslContext()); + if (handshakeTimeout.isPresent()) { + sslBuilder.handshakeTimeout(handshakeTimeout.get()); + } + }); + } else if (handshakeTimeout.isPresent()) { + httpClient = httpClient.secure(sslSpecification -> { + SslProvider.Builder sslBuilder = sslSpecification.sslContext(buildDefaultClientSslContext()); + sslBuilder.handshakeTimeout(handshakeTimeout.get()); + }); + } else { + httpClient = httpClient.secure(); + } + + return httpClient; + } + + private static SslContext buildDefaultClientSslContext() { + try { + return SslContextBuilder.forClient() + .build(); + } catch (SSLException e) { + throw new IllegalStateException(Messages.ERROR_OCCURRED_SETTING_UP_DEFAULT_SSL_CONTEXT, e); + } + } + + private static SslContext buildTrustAllSslContext() { + try { + return SslContextBuilder.forClient() + .trustManager(getTrustAllCertificatesManager()) + .build(); + } catch (SSLException e) { + throw new IllegalStateException(Messages.ERROR_OCCURRED_SETTING_UP_ALWAYS_APPROVING_SSL_CONTEXT, e); + } + } + + private static X509TrustManager getTrustAllCertificatesManager() { + return new X509TrustManager() { + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; + } + + }; + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientFactory.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientFactory.java index d2258ed03c..32c8c45c33 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientFactory.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientFactory.java @@ -1,24 +1,39 @@ package org.cloudfoundry.multiapps.controller.client.facade.rest; -import org.cloudfoundry.multiapps.controller.client.facade.CloudCredentials; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.CloudFoundryClientFactory; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableCloudFoundryClientFactory; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; -import org.cloudfoundry.multiapps.controller.client.facade.util.RestUtil; -import org.cloudfoundry.client.CloudFoundryClient; -import org.immutables.value.Value; - +import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.text.MessageFormat; import java.time.Duration; import java.util.Collections; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudCredentials; +import org.cloudfoundry.multiapps.controller.client.facade.CloudException; +import org.cloudfoundry.multiapps.controller.client.facade.adapters.LogCacheClient; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; +import org.cloudfoundry.multiapps.controller.client.facade.util.JsonUtil; +import org.cloudfoundry.multiapps.controller.client.facade.util.RestUtil; +import org.immutables.value.Value; +import org.springframework.web.client.RestClient; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; @Value.Immutable public abstract class CloudControllerRestClientFactory { + private final RestUtil restUtil = new RestUtil(); + private final Map cachedHttpConnectionPair = new ConcurrentHashMap<>(); + public abstract Optional getSslHandshakeTimeout(); public abstract Optional getConnectTimeout(); @@ -34,38 +49,165 @@ public boolean shouldTrustSelfSignedCertificates() { return false; } - @Value.Derived - public CloudFoundryClientFactory getCloudFoundryClientFactory() { - ImmutableCloudFoundryClientFactory.Builder builder = ImmutableCloudFoundryClientFactory.builder(); - getSslHandshakeTimeout().ifPresent(builder::sslHandshakeTimeout); - getConnectTimeout().ifPresent(builder::connectTimeout); - getConnectionPoolSize().ifPresent(builder::connectionPoolSize); - getThreadPoolSize().ifPresent(builder::threadPoolSize); - getResponseTimeout().ifPresent(builder::responseTimeout); - return builder.build(); - } - public CloudControllerRestClient createClient(URL controllerUrl, CloudCredentials credentials, String organizationName, String spaceName, OAuthClient oAuthClient, Map requestTags) { oAuthClient.init(credentials); - CloudSpaceClient spaceGetter = getCloudFoundryClientFactory().createSpaceClient(controllerUrl, oAuthClient, requestTags); - CloudSpace target = spaceGetter.getSpace(organizationName, spaceName); + CloudSpace target = createSpaceClient(controllerUrl, oAuthClient, requestTags).getSpace(organizationName, spaceName); return createClient(controllerUrl, credentials, target, oAuthClient, requestTags); } public CloudControllerRestClient createClient(URL controllerUrl, CloudCredentials credentials, CloudSpace target) { - return createClient(controllerUrl, credentials, target, createOAuthClient(controllerUrl, credentials.getOrigin()), + return createClient(controllerUrl, credentials, target, createOAuthClient(controllerUrl), Collections.emptyMap()); } public CloudControllerRestClient createClient(URL controllerUrl, CloudCredentials credentials, CloudSpace target, OAuthClient oAuthClient, Map requestTags) { oAuthClient.init(credentials); - CloudFoundryClient delegate = getCloudFoundryClientFactory().createClient(controllerUrl, oAuthClient, requestTags); - return new CloudControllerRestClientImpl(delegate, target); + HttpConnectionPair httpConnectionPair = getCachedHttpConnectionPair(controllerUrl, requestTags); + URL v3ApiUrl = httpConnectionPair.v3ApiUrl(); + URL baseUrl = extractBaseUrl(v3ApiUrl); + + HttpClient httpClient = httpConnectionPair.httpClient(); + RestClient restClient = CloudControllerRestClientBuilder.buildRestClient(httpClient, baseUrl, oAuthClient, requestTags); + WebClient webClient = CloudControllerRestClientBuilder.buildWebClient(httpClient, baseUrl, oAuthClient, requestTags); + + CloudControllerV3Client client = new CloudControllerV3Client(restClient, webClient); + + Function uploadRestClientFactory = uploadTimeout -> { + ClientConfigurationOptions uploadOptions = new ClientConfigurationOptions(getConnectTimeout(), Optional.of(uploadTimeout), + getSslHandshakeTimeout(), getConnectionPoolSize(), + Optional.empty(), + shouldTrustSelfSignedCertificates()); + return CloudControllerRestClientBuilder.build(baseUrl, oAuthClient, requestTags, uploadOptions); + }; + + return new CloudControllerRestClientV3Impl(target, client, uploadRestClientFactory); + } + + public CloudSpaceClient createSpaceClient(URL controllerUrl, OAuthClient oAuthClient, Map requestTags) { + HttpConnectionPair httpConnectionPair = getCachedHttpConnectionPair(controllerUrl, requestTags); + URL baseUrl = extractBaseUrl(httpConnectionPair.v3ApiUrl()); + + RestClient restClient = CloudControllerRestClientBuilder.buildRestClient(httpConnectionPair.httpClient(), baseUrl, oAuthClient, + requestTags); + WebClient webClient = CloudControllerRestClientBuilder.buildWebClient(httpConnectionPair.httpClient(), baseUrl, oAuthClient, + requestTags); + + return new CloudSpaceClient(new CloudControllerV3Client(restClient, webClient)); + } + + public LogCacheClient createLogCacheClient(URL controllerUrl, OAuthClient oAuthClient, Map requestTags) { + HttpConnectionPair httpConnectionPair = getCachedHttpConnectionPair(controllerUrl, requestTags); + RestClient restClient = CloudControllerRestClientBuilder.buildRestClient(httpConnectionPair.httpClient(), + httpConnectionPair.logCacheUrl(), + oAuthClient, requestTags); + return new LogCacheClient(restClient); + } + + private HttpConnectionPair getCachedHttpConnectionPair(URL controllerUrl, Map requestTags) { + return cachedHttpConnectionPair.computeIfAbsent(controllerUrl.getHost(), host -> { + HttpClient httpClient = CloudControllerRestClientBuilder.buildHttpClient(getClientConfigurationOptions()); + URL v3ApiUrl = resolveV3ApiUrl(controllerUrl, requestTags); + URL logCacheUrl = resolveLogCacheUrl(controllerUrl, requestTags); + return new HttpConnectionPair(httpClient, v3ApiUrl, logCacheUrl); + }); + } + + private ClientConfigurationOptions getClientConfigurationOptions() { + return new ClientConfigurationOptions(getConnectTimeout(), getResponseTimeout(), getSslHandshakeTimeout(), + getConnectionPoolSize(), getThreadPoolSize(), + shouldTrustSelfSignedCertificates()); + } + + private URL extractBaseUrl(URL url) { + try { + int port = url.getPort(); + String origin = url.getProtocol() + Constants.PROTOCOL_SEPARATOR + url.getHost() + (port == Constants.UNDEFINED_PORT + ? Constants.EMPTY_STRING + : Constants.COLON + port); + return URI.create(origin) + .toURL(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(MessageFormat.format(Messages.COULD_NOT_DETERMINE_API_ORIGIN_FROM_0, url), e); + } + } + + private URL resolveV3ApiUrl(URL controllerUrl, Map requestTags) { + try { + Map rootDocumentLinks = getCloudControllerRootDocumentLinks(controllerUrl, requestTags); + Map cloudControllerUrlV3 = (Map) rootDocumentLinks.get( + Constants.CLOUD_CONTROLLER_CF_ROOT_DOCUMENT_NAME); + return URI.create((String) cloudControllerUrlV3.get(Constants.HREF)) + .toURL(); + } catch (Exception e) { + return fallbackUrl(controllerUrl, Constants.CF_API_V3); + } + } + + private URL resolveLogCacheUrl(URL controllerUrl, Map requestTags) { + try { + Map rootDocumentLinks = getCloudControllerRootDocumentLinks(controllerUrl, requestTags); + Map logCacheUrl = (Map) rootDocumentLinks.get(Constants.LOG_CACHE_CF_ROOT_DOCUMENT_NAME); + return URI.create((String) logCacheUrl.get(Constants.HREF)) + .toURL(); + } catch (Exception e) { + String host = controllerUrl.getHost(); + String logCacheHost = host.startsWith(Constants.API_HOST_PREFIX) ? Constants.LOG_CACHE_PREFIX + host.substring( + Constants.API_HOST_PREFIX.length()) : Constants.LOG_CACHE_PREFIX + host; + try { + return URI.create(controllerUrl.getProtocol() + Constants.PROTOCOL_SEPARATOR + logCacheHost) + .toURL(); + } catch (MalformedURLException me) { + throw new CloudException(MessageFormat.format(Messages.COULD_NOT_RESOLVE_LOG_CACHE_URL_FROM_0, controllerUrl), me); + } + } + } + + @SuppressWarnings("unchecked") + private Map getCloudControllerRootDocumentLinks(URL controllerUrl, Map requestTags) { + try { + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() + .GET() + .uri(controllerUrl.toURI()) + .timeout(Constants.DEFAULT_CONNECT_TIMEOUT); + requestTags.forEach(requestBuilder::header); + HttpResponse response = getSimpleHttpClient().send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() / 100 != 2) { + throw new CloudException( + MessageFormat.format(Messages.CF_ROOT_DOCUMENT_REQUEST_TO_0_RETURNED_1, controllerUrl, response.statusCode())); + } + + return (Map) JsonUtil.convertJsonToMap(response.body()) + .get(Constants.ROOT_DOCUMENT_LINKS_LIST); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + throw new CloudException(MessageFormat.format(Messages.INTERRUPTED_WHILE_CALLING_THE_CF_ROOT_URL_AT_0, controllerUrl), e); + } catch (Exception e) { + throw new CloudException(MessageFormat.format(Messages.FAILED_TO_CALL_THE_CF_ROOT_AT_0_WITH_1, controllerUrl, e.getMessage()), + e); + } + } + + private static java.net.http.HttpClient getSimpleHttpClient() { + return java.net.http.HttpClient.newBuilder() + .connectTimeout(Constants.DEFAULT_CONNECT_TIMEOUT) + .build(); + } + + private URL fallbackUrl(URL controllerUrl, String pathSuffix) { + try { + return URI.create(controllerUrl.toString() + pathSuffix) + .toURL(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException( + MessageFormat.format(Messages.COULD_NOT_RESOLVE_URL_FROM_0_1, controllerUrl, pathSuffix), e); + } } - private OAuthClient createOAuthClient(URL controllerUrl, String origin) { + private OAuthClient createOAuthClient(URL controllerUrl) { return restUtil.createOAuthClientByControllerUrl(controllerUrl, shouldTrustSelfSignedCertificates()); } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java deleted file mode 100644 index c1e6718d16..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java +++ /dev/null @@ -1,2621 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.rest; - -import java.nio.file.Path; -import java.text.MessageFormat; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.cloudfoundry.AbstractCloudFoundryException; -import org.cloudfoundry.client.CloudFoundryClient; -import org.cloudfoundry.client.v3.BuildpackData; -import org.cloudfoundry.client.v3.DockerData; -import org.cloudfoundry.client.v3.Lifecycle; -import org.cloudfoundry.client.v3.LifecycleType; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.Resource; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.applications.Application; -import org.cloudfoundry.client.v3.applications.ApplicationFeature; -import org.cloudfoundry.client.v3.applications.ApplicationRelationships; -import org.cloudfoundry.client.v3.applications.ApplicationState; -import org.cloudfoundry.client.v3.applications.CreateApplicationRequest; -import org.cloudfoundry.client.v3.applications.CreateApplicationResponse; -import org.cloudfoundry.client.v3.applications.DeleteApplicationRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationCurrentDropletRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationCurrentDropletResponse; -import org.cloudfoundry.client.v3.applications.GetApplicationEnvironmentVariablesRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationEnvironmentVariablesResponse; -import org.cloudfoundry.client.v3.applications.GetApplicationProcessRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationProcessResponse; -import org.cloudfoundry.client.v3.applications.GetApplicationProcessStatisticsRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationProcessStatisticsResponse; -import org.cloudfoundry.client.v3.applications.GetApplicationRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationSshEnabledRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationSshEnabledResponse; -import org.cloudfoundry.client.v3.applications.ListApplicationBuildsRequest; -import org.cloudfoundry.client.v3.applications.ListApplicationFeaturesRequest; -import org.cloudfoundry.client.v3.applications.ListApplicationPackagesRequest; -import org.cloudfoundry.client.v3.applications.ListApplicationRoutesRequest; -import org.cloudfoundry.client.v3.applications.ListApplicationsRequest; -import org.cloudfoundry.client.v3.applications.ScaleApplicationRequest; -import org.cloudfoundry.client.v3.applications.SetApplicationCurrentDropletRequest; -import org.cloudfoundry.client.v3.applications.StartApplicationRequest; -import org.cloudfoundry.client.v3.applications.StopApplicationRequest; -import org.cloudfoundry.client.v3.applications.UpdateApplicationEnvironmentVariablesRequest; -import org.cloudfoundry.client.v3.applications.UpdateApplicationFeatureRequest; -import org.cloudfoundry.client.v3.applications.UpdateApplicationRequest; -import org.cloudfoundry.client.v3.applications.UpdateApplicationResponse; -import org.cloudfoundry.client.v3.auditevents.AuditEventResource; -import org.cloudfoundry.client.v3.auditevents.ListAuditEventsRequest; -import org.cloudfoundry.client.v3.builds.Build; -import org.cloudfoundry.client.v3.builds.CreateBuildRequest; -import org.cloudfoundry.client.v3.builds.GetBuildRequest; -import org.cloudfoundry.client.v3.builds.ListBuildsRequest; -import org.cloudfoundry.client.v3.domains.CreateDomainRequest; -import org.cloudfoundry.client.v3.domains.DeleteDomainRequest; -import org.cloudfoundry.client.v3.domains.Domain; -import org.cloudfoundry.client.v3.domains.DomainRelationships; -import org.cloudfoundry.client.v3.domains.DomainResource; -import org.cloudfoundry.client.v3.domains.ListDomainsRequest; -import org.cloudfoundry.client.v3.jobs.GetJobRequest; -import org.cloudfoundry.client.v3.organizations.GetOrganizationDefaultDomainRequest; -import org.cloudfoundry.client.v3.organizations.ListOrganizationDomainsRequest; -import org.cloudfoundry.client.v3.packages.CreatePackageRequest; -import org.cloudfoundry.client.v3.packages.CreatePackageResponse; -import org.cloudfoundry.client.v3.packages.GetPackageRequest; -import org.cloudfoundry.client.v3.packages.Package; -import org.cloudfoundry.client.v3.packages.PackageRelationships; -import org.cloudfoundry.client.v3.packages.PackageResource; -import org.cloudfoundry.client.v3.packages.PackageType; -import org.cloudfoundry.client.v3.packages.UploadPackageRequest; -import org.cloudfoundry.client.v3.processes.Data; -import org.cloudfoundry.client.v3.processes.HealthCheck; -import org.cloudfoundry.client.v3.processes.HealthCheckType; -import org.cloudfoundry.client.v3.processes.ReadinessHealthCheck; -import org.cloudfoundry.client.v3.processes.ReadinessHealthCheckType; -import org.cloudfoundry.client.v3.processes.UpdateProcessRequest; -import org.cloudfoundry.client.v3.roles.ListRolesRequest; -import org.cloudfoundry.client.v3.roles.RoleResource; -import org.cloudfoundry.client.v3.roles.RoleType; -import org.cloudfoundry.client.v3.routes.CreateRouteRequest; -import org.cloudfoundry.client.v3.routes.CreateRouteResponse; -import org.cloudfoundry.client.v3.routes.DeleteRouteRequest; -import org.cloudfoundry.client.v3.routes.Destination; -import org.cloudfoundry.client.v3.routes.InsertRouteDestinationsRequest; -import org.cloudfoundry.client.v3.routes.ListRoutesRequest; -import org.cloudfoundry.client.v3.routes.RemoveRouteDestinationsRequest; -import org.cloudfoundry.client.v3.routes.RouteRelationships; -import org.cloudfoundry.client.v3.routes.RouteResource; -import org.cloudfoundry.client.v3.servicebindings.CreateServiceBindingRequest; -import org.cloudfoundry.client.v3.servicebindings.CreateServiceBindingResponse; -import org.cloudfoundry.client.v3.servicebindings.DeleteServiceBindingRequest; -import org.cloudfoundry.client.v3.servicebindings.GetServiceBindingDetailsRequest; -import org.cloudfoundry.client.v3.servicebindings.GetServiceBindingDetailsResponse; -import org.cloudfoundry.client.v3.servicebindings.GetServiceBindingParametersRequest; -import org.cloudfoundry.client.v3.servicebindings.GetServiceBindingParametersResponse; -import org.cloudfoundry.client.v3.servicebindings.ListServiceBindingsRequest; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingRelationships; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingType; -import org.cloudfoundry.client.v3.servicebindings.UpdateServiceBindingRequest; -import org.cloudfoundry.client.v3.servicebrokers.BasicAuthentication; -import org.cloudfoundry.client.v3.servicebrokers.CreateServiceBrokerRequest; -import org.cloudfoundry.client.v3.servicebrokers.DeleteServiceBrokerRequest; -import org.cloudfoundry.client.v3.servicebrokers.ListServiceBrokersRequest; -import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerRelationships; -import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerResource; -import org.cloudfoundry.client.v3.servicebrokers.UpdateServiceBrokerRequest; -import org.cloudfoundry.client.v3.serviceinstances.CreateServiceInstanceRequest; -import org.cloudfoundry.client.v3.serviceinstances.DeleteServiceInstanceRequest; -import org.cloudfoundry.client.v3.serviceinstances.GetManagedServiceParametersRequest; -import org.cloudfoundry.client.v3.serviceinstances.GetManagedServiceParametersResponse; -import org.cloudfoundry.client.v3.serviceinstances.GetServiceInstanceRequest; -import org.cloudfoundry.client.v3.serviceinstances.GetUserProvidedCredentialsRequest; -import org.cloudfoundry.client.v3.serviceinstances.GetUserProvidedCredentialsResponse; -import org.cloudfoundry.client.v3.serviceinstances.ListServiceInstancesRequest; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstance; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceRelationships; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceResource; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; -import org.cloudfoundry.client.v3.serviceinstances.UpdateServiceInstanceRequest; -import org.cloudfoundry.client.v3.serviceofferings.GetServiceOfferingRequest; -import org.cloudfoundry.client.v3.serviceofferings.ListServiceOfferingsRequest; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOffering; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingResource; -import org.cloudfoundry.client.v3.serviceplans.GetServicePlanRequest; -import org.cloudfoundry.client.v3.serviceplans.ListServicePlansRequest; -import org.cloudfoundry.client.v3.serviceplans.ServicePlan; -import org.cloudfoundry.client.v3.serviceplans.ServicePlanResource; -import org.cloudfoundry.client.v3.serviceplans.UpdateServicePlanVisibilityRequest; -import org.cloudfoundry.client.v3.serviceplans.Visibility; -import org.cloudfoundry.client.v3.spaces.DeleteUnmappedRoutesRequest; -import org.cloudfoundry.client.v3.stacks.ListStacksRequest; -import org.cloudfoundry.client.v3.stacks.Stack; -import org.cloudfoundry.client.v3.tasks.CancelTaskRequest; -import org.cloudfoundry.client.v3.tasks.CreateTaskRequest; -import org.cloudfoundry.client.v3.tasks.GetTaskRequest; -import org.cloudfoundry.client.v3.tasks.ListTasksRequest; -import org.cloudfoundry.client.v3.tasks.Task; -import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; -import org.cloudfoundry.multiapps.controller.client.facade.Constants; -import org.cloudfoundry.multiapps.controller.client.facade.Messages; -import org.cloudfoundry.multiapps.controller.client.facade.UploadStatusCallback; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudAsyncJob; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudDomain; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudEvent; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudPackage; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudProcess; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudRoute; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudServiceBinding; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudServiceBroker; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudServiceKey; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudServiceOffering; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudServicePlan; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudStack; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawCloudTask; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawInstancesInfo; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawUserRole; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableRawV3CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.BitsData; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEntity; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudPackage; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudRoute; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBroker; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceOffering; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudStack; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudTask; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerCredentials; -import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ErrorDetails; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDropletInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableErrorDetails; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableInstancesInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableUpload; -import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.RouteDestination; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Status; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Upload; -import org.cloudfoundry.multiapps.controller.client.facade.domain.UserRole; -import org.cloudfoundry.multiapps.controller.client.facade.dto.ApplicationToCreateDto; -import org.cloudfoundry.multiapps.controller.client.facade.util.JobV3Util; -import org.cloudfoundry.util.PaginationUtils; -import org.springframework.http.HttpStatus; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * Abstract implementation of the CloudControllerClient intended to serve as the base. - */ -public class CloudControllerRestClientImpl implements CloudControllerRestClient { - - private static final long PACKAGE_UPLOAD_JOB_POLLING_PERIOD = TimeUnit.SECONDS.toMillis(5); - private static final Duration DELETE_JOB_TIMEOUT = Duration.ofMinutes(5); - private static final Duration BINDING_OPERATIONS_TIMEOUT = Duration.ofMinutes(10); - private static final int MAX_CHAR_LENGTH_FOR_PARAMS_IN_REQUEST = 4000; - - private CloudSpace target; // optional, as some operations do not require a targeted space - private CloudFoundryClient delegate; - - /** - * Only for unit tests. This works around the fact that the initialize method is called within the constructor and hence can not be - * overloaded, making it impossible to write unit tests that don't trigger network calls. - */ - protected CloudControllerRestClientImpl() { - } - - public CloudControllerRestClientImpl(CloudFoundryClient delegate) { - this(delegate, null); - } - - public CloudControllerRestClientImpl(CloudFoundryClient delegate, CloudSpace target) { - this.target = target; - this.delegate = delegate; - } - - @Override - public CloudSpace getTarget() { - return target; - } - - @Override - public void addDomain(String domainName) { - assertSpaceProvided("add domain"); - CloudDomain domain = findDomainByName(domainName); - if (domain == null) { - doCreateDomain(domainName); - } - } - - @Override - public void addRoute(String host, String domainName, String path) { - assertSpaceProvided("add route for domain"); - UUID domainGuid = getRequiredDomainGuid(domainName); - doAddRoute(domainGuid, host, path); - } - - @Override - public Optional bindServiceInstance(String bindingName, String applicationName, String serviceInstanceName) { - return bindServiceInstance(bindingName, applicationName, serviceInstanceName, null); - } - - @Override - public Optional bindServiceInstance(String bindingName, String applicationName, String serviceInstanceName, - Map parameters) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceInstanceName); - - var createBindingRequest = CreateServiceBindingRequest.builder() - .name(bindingName) - .type(ServiceBindingType.APPLICATION) - .relationships(ServiceBindingRelationships.builder() - .application(buildToOneRelationship( - applicationGuid)) - .serviceInstance( - buildToOneRelationship( - serviceInstanceGuid)) - .build()); - if (!CollectionUtils.isEmpty(parameters)) { - createBindingRequest.parameters(parameters); - } - return delegate.serviceBindingsV3() - .create(createBindingRequest.build()) - .map(CreateServiceBindingResponse::getJobId) - .block(); - } - - @Override - public void createApplication(ApplicationToCreateDto applicationToCreateDto) { - assertSpaceProvided("create application"); - CreateApplicationRequest createApplicationRequest = CreateApplicationRequest.builder() - .name(applicationToCreateDto.getName()) - .metadata(applicationToCreateDto.getMetadata()) - .lifecycle(buildApplicationLifecycle( - applicationToCreateDto.getStaging())) - .relationships(buildApplicationRelationships()) - .environmentVariables(applicationToCreateDto.getEnv()) - .build(); - CreateApplicationResponse createApplicationResponse = delegate.applicationsV3() - .create(createApplicationRequest) - .block(); - updateApplicationAttributes(applicationToCreateDto, createApplicationResponse.getId()); - } - - private Lifecycle buildApplicationLifecycle(Staging staging) { - // Prioritize Docker lifecycle if DockerInfo is provided - if (staging.getDockerInfo() != null) { - return createDockerLifecycle(); - } - - // Determine lifecycle type, defaulting to BUILDPACK if lifecycleType is null - LifecycleType lifecycleType = staging.getLifecycleType() != null ? LifecycleType.valueOf(staging.getLifecycleType() - .name()) : LifecycleType.BUILDPACK; - - return createLifecycleByType(staging, lifecycleType); - } - - private Lifecycle createLifecycleByType(Staging staging, LifecycleType lifecycleType) { - validateLifecycleConfiguration(staging, lifecycleType); - BuildpackData buildpackData = createBuildpackData(staging); - return Lifecycle.builder() - .type(lifecycleType) - .data(buildpackData) - .build(); - } - - private Lifecycle createDockerLifecycle() { - return Lifecycle.builder() - .type(LifecycleType.DOCKER) - .data(DockerData.builder() - .build()) - .build(); - } - - private void validateLifecycleConfiguration(Staging staging, LifecycleType lifecycleType) { - if (lifecycleType == LifecycleType.CNB && CollectionUtils.isEmpty(staging.getBuildpacks())) { - throw new IllegalArgumentException(Messages.BUILDPACKS_ARE_REQUIRED_FOR_CNB_LIFECYCLE_TYPE); - } - } - - private BuildpackData createBuildpackData(Staging staging) { - BuildpackData.Builder buildpackDataBuilder = BuildpackData.builder() - .stack(staging.getStackName()); - if (staging.getBuildpacks() != null) { - buildpackDataBuilder.addAllBuildpacks(staging.getBuildpacks()); - } - return buildpackDataBuilder.build(); - } - - private ApplicationRelationships buildApplicationRelationships() { - return ApplicationRelationships.builder() - .space(buildToOneRelationship(getTargetSpaceGuid())) - .build(); - } - - private void updateApplicationAttributes(ApplicationToCreateDto applicationToCreateDto, String applicationGuid) { - UUID createdApplicationGuid = UUID.fromString(applicationGuid); - updateApplicationProcess(createdApplicationGuid, applicationToCreateDto.getStaging()); - delegate.applicationsV3() - .scale(ScaleApplicationRequest.builder() - .applicationId(createdApplicationGuid.toString()) - .type("web") - .memoryInMb(applicationToCreateDto.getMemoryInMb()) - .diskInMb(applicationToCreateDto.getDiskQuotaInMb()) - .build()) - .block(); - if (!CollectionUtils.isEmpty(applicationToCreateDto.getRoutes())) { - addRoutes(applicationToCreateDto.getRoutes(), createdApplicationGuid); - } - } - - private void updateApplicationProcess(UUID applicationGuid, Staging staging) { - staging.getAppFeatures() - .entrySet() - .forEach(entry -> updateAppFeature(applicationGuid, entry.getKey(), entry.getValue())); - GetApplicationProcessResponse getApplicationProcessResponse = getApplicationProcessResource(applicationGuid); - UpdateProcessRequest.Builder updateProcessRequestBuilder = UpdateProcessRequest.builder() - .processId(getApplicationProcessResponse.getId()) - .command(staging.getCommand()); - if (staging.getHealthCheckType() != null) { - updateProcessRequestBuilder.healthCheck(buildHealthCheck(staging)); - } - if (staging.getReadinessHealthCheckType() != null) { - updateProcessRequestBuilder.readinessHealthCheck(buildReadinessHealthCheck(staging)); - } - delegate.processes() - .update(updateProcessRequestBuilder.build()) - .block(); - } - - private ReadinessHealthCheck buildReadinessHealthCheck(Staging staging) { - return ReadinessHealthCheck.builder() - .type(ReadinessHealthCheckType.from(staging.getReadinessHealthCheckType())) - .data(buildReadinessHealthCheckData(staging)) - .build(); - } - - private Data buildReadinessHealthCheckData(Staging staging) { - return Data.builder() - .invocationTimeout(staging.getReadinessHealthCheckInvocationTimeout()) - .endpoint(staging.getReadinessHealthCheckHttpEndpoint()) - .interval(staging.getReadinessHealthCheckInterval()) - .build(); - } - - private void updateAppFeature(UUID applicationGuid, String featureName, boolean enabled) { - delegate.applicationsV3() - .updateFeature(UpdateApplicationFeatureRequest.builder() - .featureName(featureName) - .enabled(enabled) - .applicationId(applicationGuid.toString()) - .build()) - .block(); - } - - private GetApplicationProcessResponse getApplicationProcessResource(UUID applicationGuid) { - return delegate.applicationsV3() - .getProcess(GetApplicationProcessRequest.builder() - .type("web") - .applicationId(applicationGuid.toString()) - .build()) - .block(); - } - - private HealthCheck buildHealthCheck(Staging staging) { - HealthCheckType healthCheckType = HealthCheckType.from(staging.getHealthCheckType()); - return HealthCheck.builder() - .type(healthCheckType) - .data(Data.builder() - .endpoint(staging.getHealthCheckHttpEndpoint()) - .timeout(staging.getHealthCheckTimeout()) - .invocationTimeout(staging.getInvocationTimeout()) - .interval(staging.getHealthCheckInterval()) - .build()) - .build(); - } - - private void addRoutes(Set routes, UUID applicationGuid) { - Map domains = getDomainsFromRoutes(routes); - for (CloudRoute route : routes) { - validateDomainForRoute(route, domains); - UUID domainGuid = domains.get(route.getDomain() - .getName()); - UUID routeGuid = getOrAddRoute(domainGuid, route.getHost(), route.getPath()); - bindRoute(routeGuid, applicationGuid, route.getRequestedProtocol()); - } - } - - @Override - public void createServiceInstance(CloudServiceInstance serviceInstance) { - assertSpaceProvided("create service instance"); - Assert.notNull(serviceInstance, "Service instance must not be null."); - CloudServicePlan servicePlan = findPlanForService(serviceInstance, serviceInstance.getPlan()); - UUID servicePlanGuid = servicePlan.getGuid(); - - delegate.serviceInstancesV3() - .create(CreateServiceInstanceRequest.builder() - .type(ServiceInstanceType.MANAGED) - .name(serviceInstance.getName()) - .metadata(serviceInstance.getV3Metadata()) - .relationships(ServiceInstanceRelationships.builder() - .servicePlan(buildToOneRelationship( - servicePlanGuid.toString())) - .space(buildToOneRelationship( - getTargetSpaceGuid().toString())) - .build()) - .tags(serviceInstance.getTags()) - .parameters(serviceInstance.getCredentials()) - .build()) - .block(); - } - - @Override - public String createServiceBroker(CloudServiceBroker serviceBroker) { - Assert.notNull(serviceBroker, "Service broker must not be null."); - - ServiceBrokerRelationships serviceBrokerRelationship = Optional.ofNullable(serviceBroker.getSpaceGuid()) - .map(UUID::fromString) - .map(this::buildToOneRelationship) - .map(spaceRelationship -> ServiceBrokerRelationships.builder() - .space( - spaceRelationship) - .build()) - .orElse(null); - - return delegate.serviceBrokersV3() - .create(CreateServiceBrokerRequest.builder() - .name(serviceBroker.getName()) - .url(serviceBroker.getUrl()) - .authentication(BasicAuthentication.builder() - .username(serviceBroker.getUsername()) - .password(serviceBroker.getPassword()) - .build()) - .relationships(serviceBrokerRelationship) - .build()) - .block(); - } - - @Override - public CloudServiceKey createAndFetchServiceKey(CloudServiceKey keyModel, String serviceInstanceName) { - CloudServiceInstance serviceInstance = getServiceInstance(serviceInstanceName); - doCreateServiceKeySync(keyModel.getName(), keyModel.getCredentials(), keyModel.getV3Metadata(), serviceInstance); - - return fetchWithAuxiliaryContent( - () -> getServiceKeyResourceByNameAndServiceInstanceGuid(keyModel.getName(), getGuid(serviceInstance)), - fetchedKey -> zipWithAuxiliaryServiceKeyContent(fetchedKey, serviceInstance)); - } - - @Override - public Optional createServiceKey(CloudServiceKey keyModel, String serviceInstanceName) { - CloudServiceInstance serviceInstance = getServiceInstance(serviceInstanceName); - return doCreateServiceKey(keyModel.getName(), keyModel.getCredentials(), keyModel.getV3Metadata(), serviceInstance); - } - - @Override - public Optional createServiceKey(String serviceInstanceName, String serviceKeyName, Map parameters) { - CloudServiceInstance serviceInstance = getServiceInstance(serviceInstanceName); - return doCreateServiceKey(serviceKeyName, parameters, null, serviceInstance); - } - - private void doCreateServiceKeySync(String name, Map parameters, Metadata metadata, - CloudServiceInstance serviceInstance) { - CreateServiceBindingRequest createServiceBindingRequest = buildServiceCredentialBindingRequest(name, parameters, metadata, - serviceInstance); - delegate.serviceBindingsV3() - .create(createServiceBindingRequest) - .map(response -> response.getJobId() - .get()) - .flatMap(jobId -> JobV3Util.waitForCompletion(delegate, BINDING_OPERATIONS_TIMEOUT, jobId)) - .block(); - } - - private Optional doCreateServiceKey(String name, Map parameters, Metadata metadata, - CloudServiceInstance serviceInstance) { - CreateServiceBindingRequest createServiceBindingRequest = buildServiceCredentialBindingRequest(name, parameters, metadata, - serviceInstance); - return delegate.serviceBindingsV3() - .create(createServiceBindingRequest) - .map(CreateServiceBindingResponse::getJobId) - .block(); - } - - private CreateServiceBindingRequest buildServiceCredentialBindingRequest(String name, Map parameters, Metadata metadata, - CloudServiceInstance serviceInstance) { - if (serviceInstance.getType() != ServiceInstanceType.MANAGED) { - throw new IllegalArgumentException( - String.format(Messages.CANT_CREATE_SERVICE_KEY_FOR_USER_PROVIDED_SERVICE, serviceInstance.getName())); - } - UUID serviceInstanceGuid = getGuid(serviceInstance); - - var createBindingRequest = CreateServiceBindingRequest.builder() - .type(ServiceBindingType.KEY) - .name(name) - .metadata(metadata) - .relationships(ServiceBindingRelationships.builder() - .serviceInstance( - buildToOneRelationship( - serviceInstanceGuid)) - .build()); - if (!CollectionUtils.isEmpty(parameters)) { - createBindingRequest.parameters(parameters); - } - return createBindingRequest.build(); - } - - @Override - public void createUserProvidedServiceInstance(CloudServiceInstance serviceInstance) { - assertSpaceProvided("create service instance"); - Assert.notNull(serviceInstance, "Service instance must not be null."); - String syslogDrainUrl = StringUtils.hasText(serviceInstance.getSyslogDrainUrl()) ? serviceInstance.getSyslogDrainUrl() : ""; - delegate.serviceInstancesV3() - .create(CreateServiceInstanceRequest.builder() - .name(serviceInstance.getName()) - .metadata(serviceInstance.getV3Metadata()) - .type(ServiceInstanceType.USER_PROVIDED) - .credentials(serviceInstance.getCredentials()) - .syslogDrainUrl(syslogDrainUrl) - .tags(serviceInstance.getTags()) - .relationships(ServiceInstanceRelationships.builder() - .space(buildToOneRelationship( - getTargetSpaceGuid().toString())) - .build()) - .build()) - .block(); - } - - @Override - public void deleteApplication(String applicationName) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - deleteApplication(applicationGuid); - } - - private void deleteApplication(UUID applicationGuid) { - delegate.applicationsV3() - .delete(DeleteApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .build()) - .flatMap(jobId -> JobV3Util.waitForCompletion(delegate, DELETE_JOB_TIMEOUT, jobId)) - .block(); - } - - @Override - public void deleteDomain(String domainName) { - assertSpaceProvided("delete domain"); - CloudDomain domain = findDomainByName(domainName, true); - List routes = findRoutes(domain); - if (!routes.isEmpty()) { - throw new IllegalStateException("Unable to remove domain that is in use --" + " it has " + routes.size() + " routes."); - } - doDeleteDomain(getGuid(domain)); - } - - /** - * Delete routes that do not have any application which is assigned to them. - */ - @Override - public void deleteOrphanedRoutes() { - delegate.spacesV3() - .deleteUnmappedRoutes(DeleteUnmappedRoutesRequest.builder() - .spaceId(getTargetSpaceGuid().toString()) - .build()) - .flatMap(jobId -> JobV3Util.waitForCompletion(delegate, DELETE_JOB_TIMEOUT, jobId)) - .block(); - } - - @Override - public void deleteRoute(String host, String domainName, String path) { - assertSpaceProvided("delete route for domain"); - UUID routeGuid = getRouteGuid(getRequiredDomainGuid(domainName), host, path); - if (routeGuid == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", - "Host " + host + " not found for domain " + domainName + "."); - } - doDeleteRoute(routeGuid); - } - - @Override - public void deleteServiceInstance(String serviceInstanceName) { - CloudServiceInstance serviceInstance = getServiceInstanceWithoutAuxiliaryContent(serviceInstanceName); - doDeleteServiceInstance(serviceInstance.getGuid()); - } - - @Override - public void deleteServiceInstance(CloudServiceInstance serviceInstance) { - doDeleteServiceInstance(serviceInstance.getGuid()); - } - - @Override - public String deleteServiceBroker(String name) { - CloudServiceBroker broker = getServiceBroker(name); - UUID guid = broker.getMetadata() - .getGuid(); - return delegate.serviceBrokersV3() - .delete(DeleteServiceBrokerRequest.builder() - .serviceBrokerId(guid.toString()) - .build()) - .block(); - } - - @Override - public Optional deleteServiceBinding(String serviceInstanceName, String serviceKeyName) { - CloudServiceKey serviceKey = getServiceKey(serviceInstanceName, serviceKeyName); - if (serviceKey == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service key " + serviceKeyName + " not found."); - } - return doDeleteServiceBindings(List.of(serviceKey.getGuid())).stream() - .findFirst(); - } - - @Override - public CloudApplication getApplication(String applicationName) { - return getApplication(applicationName, true); - } - - @Override - public CloudApplication getApplication(String applicationName, boolean required) { - return findApplicationByName(applicationName, required); - } - - @Override - public UUID getApplicationGuid(String applicationName) { - return getRequiredApplicationGuid(applicationName); - } - - @Override - public String getApplicationName(UUID applicationGuid) { - // This will throw a CloudOperationException with a 404 if no app with given GUID is present - return getApplicationByGuid(applicationGuid).block() - .getName(); - } - - @Override - public Map getApplicationEnvironment(UUID applicationGuid) { - return delegate.applicationsV3() - .getEnvironmentVariables(GetApplicationEnvironmentVariablesRequest.builder() - .applicationId(applicationGuid.toString()) - .build()) - .map(GetApplicationEnvironmentVariablesResponse::getVars) - .block(); - } - - @Override - public Map getApplicationEnvironment(String applicationName) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - return getApplicationEnvironment(applicationGuid); - } - - @Override - public List getApplicationEvents(String applicationName) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - return getEventsByTarget(applicationGuid); - } - - @Override - public List getEventsByTarget(UUID uuid) { - return findEventsByTarget(uuid.toString()); - } - - @Override - public InstancesInfo getApplicationInstances(CloudApplication application) { - if (application.getState() - .equals(CloudApplication.State.STARTED)) { - return findApplicationInstances(getGuid(application)); - } - return ImmutableInstancesInfo.builder() - .instances(Collections.emptyList()) - .build(); - } - - @Override - public InstancesInfo getApplicationInstances(UUID applicationGuid) { - return findApplicationInstances(applicationGuid); - } - - @Override - public CloudProcess getApplicationProcess(UUID applicationGuid) { - return fetch(() -> Mono.just(getApplicationProcessResource(applicationGuid)), ImmutableRawCloudProcess::of); - } - - @Override - public List getApplicationRoutes(UUID applicationGuid) { - return fetchList(() -> getRouteResourcesByAppGuid(applicationGuid), routeResource -> ImmutableRawCloudRoute.builder() - .route(routeResource) - .applicationGuid( - applicationGuid) - .build()); - } - - @Override - public boolean getApplicationSshEnabled(UUID applicationGuid) { - return delegate.applicationsV3() - .getSshEnabled(GetApplicationSshEnabledRequest.builder() - .applicationId(applicationGuid.toString()) - .build()) - .map(GetApplicationSshEnabledResponse::getEnabled) - .defaultIfEmpty(false) - .block(); - } - - @Override - public Map getApplicationFeatures(UUID applicationGuid) { - IntFunction pageRequestSupplier = page -> ListApplicationFeaturesRequest.builder() - .applicationId( - applicationGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .listFeatures(pageRequestSupplier.apply(page))) - .collect(Collectors.toMap(ApplicationFeature::getName, ApplicationFeature::getEnabled)) - .block(); - } - - @Override - public List getApplications() { - return fetchList(this::getApplicationResources, application -> ImmutableRawCloudApplication.builder() - .application(application) - .space(target) - .build()); - } - - @Override - public List getApplicationsByMetadataLabelSelector(String labelSelector) { - assertSpaceProvided("get applications"); - return fetchList(() -> getApplicationsByLabelSelector(labelSelector), application -> ImmutableRawCloudApplication.builder() - .application( - application) - .space(target) - .build()); - } - - private Flux getApplicationsByLabelSelector(String labelSelector) { - IntFunction pageRequestSupplier = page -> ListApplicationsRequest.builder() - .spaceId(getTargetSpaceGuid().toString()) - .labelSelector(labelSelector) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .list(pageRequestSupplier.apply(page))); - } - - @Override - public CloudDomain getDefaultDomain() { - return fetch(() -> getDefaultDomainResource(getTargetOrganizationGuid().toString()), ImmutableRawCloudDomain::of); - } - - private Mono getDefaultDomainResource(String guid) { - return delegate.organizationsV3() - .getDefaultDomain(GetOrganizationDefaultDomainRequest.builder() - .organizationId(guid) - .build()); - } - - @Override - public List getSharedDomains() { - return fetchList(this::getSharedDomainResources, ImmutableRawCloudDomain::of); - } - - @Override - public List getDomains() { - return fetchList(this::getDomainResources, ImmutableRawCloudDomain::of); - } - - @Override - public List getDomainsForOrganization() { - assertSpaceProvided("access organization domains"); - return findDomainsByOrganizationGuid(getTargetOrganizationGuid()); - } - - @Override - public List getPrivateDomains() { - return fetchList(this::getPrivateDomainResources, ImmutableRawCloudDomain::of); - } - - @Override - public List getEvents() { - return fetchList(this::getEventResources, ImmutableRawCloudEvent::of); - } - - @Override - public List getRoutes(String domainName) { - assertSpaceProvided("get routes for domain"); - CloudDomain domain = findDomainByName(domainName, true); - return findRoutes(domain); - } - - @Override - public UUID getRequiredServiceInstanceGuid(String name) { - Resource serviceInstanceResource = getServiceInstanceByName(name).block(); - if (serviceInstanceResource == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service instance " + name + " not found."); - } - return UUID.fromString(serviceInstanceResource.getId()); - } - - @Override - public CloudServiceInstance getServiceInstance(String serviceInstanceName) { - return getServiceInstance(serviceInstanceName, true); - } - - @Override - public CloudServiceInstance getServiceInstance(String serviceInstanceName, boolean required) { - CloudServiceInstance serviceInstance = findServiceInstanceByName(serviceInstanceName); - return getServiceInstanceIfRequired(serviceInstanceName, serviceInstance, required); - } - - private CloudServiceInstance getServiceInstanceIfRequired(String serviceInstanceName, CloudServiceInstance serviceInstance, - boolean required) { - if (serviceInstance == null && required) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service instance " + serviceInstanceName + " not found."); - } - return serviceInstance; - } - - @Override - public CloudServiceBinding getServiceBinding(UUID serviceBindingGuid) { - // TODO: Refactor and use serviceBindingsV3.get() when https://github.com/cloudfoundry/cf-java-client/pull/1171 is merged and - // released - return fetch(() -> getServiceBindingResource(serviceBindingGuid), ImmutableRawCloudServiceBinding::of); - } - - private Mono getServiceBindingResource(UUID serviceBindingGuid) { - IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() - .page(page) - .id(serviceBindingGuid.toString()) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceBindingsV3() - .list(pageRequestSupplier.apply(page))) - .singleOrEmpty(); - } - - @Override - public List getServiceAppBindings(UUID serviceInstanceGuid) { - return fetchList(() -> getServiceBindingResourcesByServiceInstanceGuid(serviceInstanceGuid), ImmutableRawCloudServiceBinding::of); - } - - @Override - public List getAppBindings(UUID applicationGuid) { - return fetchList(() -> getServiceBindingResourcesByApplicationGuid(applicationGuid), ImmutableRawCloudServiceBinding::of); - } - - @Override - public List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid) { - return fetchList(() -> getServiceBindingResourcesByApplicationGuidAndServiceInstanceGuid(applicationId, serviceInstanceGuid), - ImmutableRawCloudServiceBinding::of); - } - - @Override - public CloudServiceBroker getServiceBroker(String name) { - return getServiceBroker(name, true); - } - - @Override - public CloudServiceBroker getServiceBroker(String name, boolean required) { - CloudServiceBroker serviceBroker = findServiceBrokerByName(name); - if (serviceBroker == null && required) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service broker " + name + " not found."); - } - return serviceBroker; - } - - @Override - public List getServiceBrokers() { - return fetchList(this::getServiceBrokerResources, ImmutableRawCloudServiceBroker::of); - } - - @Override - public CloudServiceKey getServiceKey(String serviceInstanceName, String serviceKeyName) { - CloudServiceInstance serviceInstance = getServiceInstance(serviceInstanceName); - return fetchWithAuxiliaryContent(() -> getServiceKeyResourceByNameAndServiceInstanceGuid(serviceKeyName, serviceInstance.getGuid()), - serviceKey -> zipWithAuxiliaryServiceKeyContent(serviceKey, serviceInstance)); - } - - @Override - public List getServiceKeys(String serviceInstanceName) { - CloudServiceInstance serviceInstance = getServiceInstance(serviceInstanceName); - return getServiceKeys(serviceInstance); - } - - @Override - public List getServiceKeysWithCredentials(String serviceInstanceName) { - CloudServiceInstance serviceInstance = getServiceInstance(serviceInstanceName); - return getServiceKeysWithCredentials(serviceInstance); - } - - @Override - public List getServiceKeys(CloudServiceInstance serviceInstance) { - return fetchList(() -> getServiceKeyResource(serviceInstance), serviceKey -> ImmutableRawCloudServiceKey.builder() - .serviceInstance( - serviceInstance) - .serviceBindingResource( - serviceKey) - .build()); - } - - @Override - public List getServiceKeysWithCredentials(CloudServiceInstance serviceInstance) { - return fetchListWithAuxiliaryContent(() -> getServiceKeyResource(serviceInstance), - serviceKey -> zipWithAuxiliaryServiceKeyContent(serviceKey, serviceInstance)); - } - - private Mono> zipWithAuxiliaryServiceKeyContent(ServiceBindingResource key, - CloudServiceInstance serviceInstance) { - return getServiceKeyCredentials(key.getId()).map(credentials -> ImmutableRawCloudServiceKey.builder() - .serviceBindingResource(key) - .credentials(credentials) - .serviceInstance(serviceInstance) - .build()); - } - - private Mono> getServiceKeyCredentials(String keyGuid) { - return delegate.serviceBindingsV3() - .getDetails(GetServiceBindingDetailsRequest.builder() - .serviceBindingId(keyGuid) - .build()) - // CF V3 API returns 404 when fetching credentials of a service key which creation failed - .onErrorResume(t -> doesErrorMatchStatusCode(t, HttpStatus.NOT_FOUND), t -> Mono.just( - GetServiceBindingDetailsResponse.builder() - .volumeMounts(Collections.emptyList()) - .credentials(Collections.emptyMap()) - .build())) - .map(GetServiceBindingDetailsResponse::getCredentials); - } - - @Override - public Map getServiceInstanceParameters(UUID guid) { - return delegate.serviceInstancesV3() - .getManagedServiceParameters(GetManagedServiceParametersRequest.builder() - .serviceInstanceId(guid.toString()) - .build()) - .map(GetManagedServiceParametersResponse::getParameters) - .block(); - } - - @Override - public Map getUserProvidedServiceInstanceParameters(UUID guid) { - return delegate.serviceInstancesV3() - .getUserProvidedCredentials(GetUserProvidedCredentialsRequest.builder() - .serviceInstanceId(guid.toString()) - .build()) - .map(GetUserProvidedCredentialsResponse::getCredentials) - .block(); - } - - @Override - public Map getServiceBindingParameters(UUID guid) { - return delegate.serviceBindingsV3() - .getParameters(GetServiceBindingParametersRequest.builder() - .serviceBindingId(guid.toString()) - .build()) - .map(GetServiceBindingParametersResponse::getParameters) - .block(); - } - - @Override - public List getServiceOfferings() { - return fetchListWithAuxiliaryContent(this::getServiceResources, this::zipWithAuxiliaryServiceOfferingContent); - } - - @Override - public void updateServicePlan(String serviceName, String planName) { - CloudServiceInstance service = getServiceInstance(serviceName); - if (service.isUserProvided()) { - return; - } - CloudServicePlan plan = findPlanForService(service, planName); - delegate.serviceInstancesV3() - .update(UpdateServiceInstanceRequest.builder() - .serviceInstanceId(service.getGuid() - .toString()) - .relationships(ServiceInstanceRelationships.builder() - .servicePlan( - buildToOneRelationship(plan.getGuid())) - .build()) - .build()) - .block(); - } - - @Override - public String getServiceInstanceName(UUID serviceInstanceGuid) { - return getServiceInstanceByGuid(serviceInstanceGuid).block() - .getName(); - } - - @Override - public CloudServiceInstance getServiceInstanceWithoutAuxiliaryContent(String serviceInstanceName) { - return getServiceInstanceWithoutAuxiliaryContent(serviceInstanceName, true); - } - - @Override - public CloudServiceInstance getServiceInstanceWithoutAuxiliaryContent(String serviceInstanceName, boolean required) { - CloudServiceInstance serviceInstance = fetch(() -> getServiceInstanceResourceByName(serviceInstanceName), - ImmutableRawCloudServiceInstance::of); - return getServiceInstanceIfRequired(serviceInstanceName, serviceInstance, required); - } - - @Override - public void updateServiceParameters(String serviceName, Map parameters) { - CloudServiceInstance service = getServiceInstanceWithoutAuxiliaryContent(serviceName); - var updateServiceInstanceRequest = UpdateServiceInstanceRequest.builder() - .serviceInstanceId(service.getGuid() - .toString()); - if (service.isUserProvided()) { - updateServiceInstanceRequest.credentials(parameters); - } else { - updateServiceInstanceRequest.parameters(parameters); - } - delegate.serviceInstancesV3() - .update(updateServiceInstanceRequest.build()) - .block(); - } - - @Override - public void updateServiceTags(String serviceName, List tags) { - UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceName); - delegate.serviceInstancesV3() - .update(UpdateServiceInstanceRequest.builder() - .serviceInstanceId(serviceInstanceGuid.toString()) - .tags(tags) - .build()) - .block(); - } - - @Override - public void updateServiceSyslogDrainUrl(String serviceName, String syslogDrainUrl) { - CloudServiceInstance service = getServiceInstanceWithoutAuxiliaryContent(serviceName); - if (!service.isUserProvided()) { - return; - } - String updatedSyslogDrain = StringUtils.hasText(syslogDrainUrl) ? syslogDrainUrl : ""; - delegate.serviceInstancesV3() - .update(UpdateServiceInstanceRequest.builder() - .serviceInstanceId(service.getGuid() - .toString()) - .syslogDrainUrl(updatedSyslogDrain) - .build()) - .block(); - } - - private List> toBatches(Collection largeList, int maxCharLength) { - if (largeList.isEmpty()) { - return Collections.emptyList(); - } - - List> batches = new ArrayList<>(); - int currentBatchLength = 0, currentBatchIndex = 0; - batches.add(new ArrayList<>()); - - for (T element : largeList) { - int elementLength = element.toString() - .length(); - if (elementLength + currentBatchLength >= maxCharLength) { - batches.add(new ArrayList<>()); - currentBatchIndex++; - currentBatchLength = 0; - } - batches.get(currentBatchIndex) - .add(element); - currentBatchLength += elementLength; - } - return batches; - } - - private Flux getServiceInstancesByNames(List serviceInstanceNames) { - String spaceGuid = getTargetSpaceGuid().toString(); - IntFunction pageRequestSupplier = page -> ListServiceInstancesRequest.builder() - .spaceId(spaceGuid) - .addAllServiceInstanceNames( - serviceInstanceNames) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceInstancesV3() - .list(pageRequestSupplier.apply(page))); - } - - @Override - public List getServiceInstancesWithoutAuxiliaryContentByNames(List names) { - List allServiceInstances = new ArrayList<>(); - for (List batchOfServiceInstanceNames : toBatches(names, MAX_CHAR_LENGTH_FOR_PARAMS_IN_REQUEST)) { - List serviceInstances = fetchList(() -> getServiceInstancesByNames(batchOfServiceInstanceNames), - ImmutableRawV3CloudServiceInstance::of); - allServiceInstances.addAll(serviceInstances); - } - return allServiceInstances; - } - - @Override - public List getServiceInstancesByMetadataLabelSelector(String labelSelector) { - IntFunction pageRequestSupplier = page -> ListServiceInstancesRequest.builder() - .labelSelector(labelSelector) - .spaceId( - getTargetSpaceGuid().toString()) - .page(page) - .build(); - - return fetchListWithAuxiliaryContent(() -> getServiceInstanceResources(pageRequestSupplier), - this::zipWithAuxiliaryServiceInstanceContent); - } - - @Override - public List getServiceInstancesWithoutAuxiliaryContentByMetadataLabelSelector(String labelSelector) { - IntFunction pageRequestSupplier = page -> ListServiceInstancesRequest.builder() - .labelSelector(labelSelector) - .spaceId( - getTargetSpaceGuid().toString()) - .page(page) - .build(); - - return fetchList(() -> getServiceInstanceResources(pageRequestSupplier), ImmutableRawCloudServiceInstance::of); - } - - @Override - public CloudStack getStack(String name) { - return getStack(name, true); - } - - @Override - public CloudStack getStack(String name, boolean required) { - CloudStack stack = findStackResource(name); - if (stack == null && required) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Stack " + name + " not found."); - } - return stack; - } - - @Override - public List getStacks() { - return fetchList(this::getStackResources, ImmutableRawCloudStack::of); - } - - @Override - public void rename(String applicationName, String newName) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - delegate.applicationsV3() - .update(UpdateApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .name(newName) - .build()) - .onErrorResume(t -> doesErrorMatchStatusCode(t, HttpStatus.SERVICE_UNAVAILABLE), - exception -> fallbackApplicationRename(newName, applicationGuid, exception)) - .block(); - } - - private Mono fallbackApplicationRename(String newName, UUID applicationGuid, Throwable exception) { - String existingApplicationName = getApplicationByGuid(applicationGuid).block() - .getName(); - if (newName.equals(existingApplicationName)) { - return Mono.empty(); - } - return Mono.error(exception); - } - - @Override - public void restartApplication(String applicationName) { - stopApplication(applicationName); - startApplication(applicationName); - } - - @Override - public void startApplication(String applicationName) { - Application application = getApplicationByName(applicationName).block(); - if (application.getState() == ApplicationState.STARTED) { - return; - } - UUID applicationGuid = UUID.fromString(application.getId()); - delegate.applicationsV3() - .start(StartApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .build()) - .block(); - } - - @Override - public void stopApplication(String applicationName) { - Application application = getApplicationByName(applicationName).block(); - if (application.getState() == ApplicationState.STOPPED) { - return; - } - UUID applicationGuid = UUID.fromString(application.getId()); - delegate.applicationsV3() - .stop(StopApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .build()) - .block(); - } - - @Override - public List unbindServiceInstance(String applicationName, String serviceInstanceName) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceInstanceName); - - return doUnbindServiceInstance(applicationGuid, serviceInstanceGuid); - } - - @Override - public Optional deleteServiceBinding(UUID serviceBindingGuid) { - return doDeleteServiceBindings(List.of(serviceBindingGuid)).stream() - .findFirst(); - } - - @Override - public List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { - return doUnbindServiceInstance(applicationGuid, serviceInstanceGuid); - } - - @Override - public void updateApplicationDiskQuota(String applicationName, int diskQuota) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - delegate.applicationsV3() - .scale(ScaleApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .type("web") - .diskInMb(diskQuota) - .build()) - .block(); - } - - @Override - public void updateApplicationEnv(String applicationName, Map env) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - delegate.applicationsV3() - .updateEnvironmentVariables(UpdateApplicationEnvironmentVariablesRequest.builder() - .applicationId(applicationGuid.toString()) - .vars(env) - .build()) - .block(); - } - - @Override - public void updateApplicationInstances(String applicationName, int instances) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - delegate.applicationsV3() - .scale(ScaleApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .type("web") - .instances(instances) - .build()) - .block(); - } - - @Override - public void updateApplicationMemory(String applicationName, int memory) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - delegate.applicationsV3() - .scale(ScaleApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .type("web") - .memoryInMb(memory) - .build()) - .block(); - } - - @Override - public void updateApplicationMetadata(UUID guid, org.cloudfoundry.client.v3.Metadata metadata) { - delegate.applicationsV3() - .update(org.cloudfoundry.client.v3.applications.UpdateApplicationRequest.builder() - .applicationId(guid.toString()) - .metadata(metadata) - .build()) - .block(); - } - - @Override - public void updateApplicationStaging(String applicationName, Staging staging) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - delegate.applicationsV3() - .update(UpdateApplicationRequest.builder() - .applicationId(applicationGuid.toString()) - .lifecycle(buildApplicationLifecycle(staging)) - .build()) - .block(); - updateApplicationProcess(applicationGuid, staging); - } - - @Override - public void updateApplicationRoutes(String applicationName, Set updatedRoutes) { - UUID applicationGuid = getApplicationGuid(applicationName); - List appRoutes = fetchList(() -> getRouteResourcesByAppGuid(applicationGuid), ImmutableRawCloudRoute::of); - Set outdatedRoutes = getOutdatedRoutes(applicationGuid, appRoutes, updatedRoutes); - Set newRoutes = getNewRoutes(applicationGuid, appRoutes, updatedRoutes); - removeRoutes(outdatedRoutes, applicationGuid); - addRoutes(newRoutes, applicationGuid); - } - - private Set getOutdatedRoutes(UUID applicationGuid, List currentRoutes, Set updatedRoutes) { - return currentRoutes.stream() - .filter(currentRoute -> isRouteOutdated(applicationGuid, currentRoute, updatedRoutes)) - .collect(Collectors.toSet()); - } - - private boolean isRouteOutdated(UUID applicationGuid, CloudRoute currentRoute, Set updatedRoutes) { - Optional updatedRoute = findRoute(currentRoute.getUrl(), updatedRoutes); - if (updatedRoute.isEmpty()) { - return true; - } - return isProtocolChanged(applicationGuid, currentRoute, updatedRoute.get()); - } - - private Optional findRoute(String url, Collection routes) { - return routes.stream() - .filter(route -> Objects.equals(url, route.getUrl())) - .findFirst(); - } - - private boolean isProtocolChanged(UUID applicationGuid, CloudRoute currentRoute, CloudRoute updatedRoute) { - if (updatedRoute.getRequestedProtocol() == null) { - return false; - } - return currentRoute.getDestinations() - .stream() - .filter(routeDestination -> Objects.equals(routeDestination.getApplicationGuid(), applicationGuid)) - .noneMatch( - routeDestination -> Objects.equals(routeDestination.getProtocol(), updatedRoute.getRequestedProtocol())); - } - - private Set getNewRoutes(UUID applicationGuid, List currentRoutes, Set updatedRoutes) { - return updatedRoutes.stream() - .filter(updatedRoute -> isRouteUpdated(applicationGuid, updatedRoute, currentRoutes)) - .collect(Collectors.toSet()); - } - - private boolean isRouteUpdated(UUID applicationGuid, CloudRoute updatedRoute, List currentRoutes) { - Optional currentRoute = findRoute(updatedRoute.getUrl(), currentRoutes); - if (currentRoute.isEmpty()) { - return true; - } - return isProtocolChanged(applicationGuid, currentRoute.get(), updatedRoute); - } - - @Override - public String updateServiceBroker(CloudServiceBroker serviceBroker) { - Assert.notNull(serviceBroker, "Service broker must not be null."); - - CloudServiceBroker existingBroker = getServiceBroker(serviceBroker.getName()); - UUID brokerGuid = existingBroker.getMetadata() - .getGuid(); - - return delegate.serviceBrokersV3() - .update(UpdateServiceBrokerRequest.builder() - .serviceBrokerId(brokerGuid.toString()) - .name(serviceBroker.getName()) - .authentication(BasicAuthentication.builder() - .username(serviceBroker.getUsername()) - .password(serviceBroker.getPassword()) - .build()) - .url(serviceBroker.getUrl()) - .build()) - .flatMap(response -> Mono.justOrEmpty(response.jobId())) - .block(); - } - - @Override - public void updateServiceInstanceMetadata(UUID guid, org.cloudfoundry.client.v3.Metadata metadata) { - delegate.serviceInstancesV3() - .update(UpdateServiceInstanceRequest.builder() - .serviceInstanceId(guid.toString()) - .metadata(metadata) - .build()) - .block(); - } - - @Override - public void updateServiceBindingMetadata(UUID guid, org.cloudfoundry.client.v3.Metadata metadata) { - delegate.serviceBindingsV3() - .update(UpdateServiceBindingRequest.builder() - .serviceBindingId(guid.toString()) - .metadata(metadata) - .build()) - .block(); - } - - @Override - public void updateServicePlanVisibilityForBroker(String name, ServicePlanVisibility visibility) { - CloudServiceBroker broker = getServiceBroker(name); - List servicePlans = findServicePlansByBrokerGuid(getGuid(broker)); - for (CloudServicePlan servicePlan : servicePlans) { - updateServicePlanVisibility(servicePlan, visibility); - } - } - - @Override - public CloudTask getTask(UUID taskGuid) { - return fetch(() -> getTaskResource(taskGuid), ImmutableRawCloudTask::of); - } - - @Override - public List getTasks(String applicationName) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - return fetchList(() -> getTaskResourcesByApplicationGuid(applicationGuid), ImmutableRawCloudTask::of); - } - - @Override - public CloudTask runTask(String applicationName, CloudTask task) { - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - return createTask(applicationGuid, task); - } - - @Override - public CloudTask cancelTask(UUID taskGuid) { - return fetch(() -> cancelTaskResource(taskGuid), ImmutableRawCloudTask::of); - } - - @Override - public CloudPackage asyncUploadApplication(String applicationName, Path file, UploadStatusCallback callback, Duration uploadTimeout) { - CloudPackage cloudPackage = startUpload(applicationName, file, uploadTimeout); - processAsyncUploadInBackground(cloudPackage, callback); - return cloudPackage; - } - - @Override - public Upload getUploadStatus(UUID packageGuid) { - CloudPackage cloudPackage = getPackage(packageGuid); - ErrorDetails errorDetails = null; - if (cloudPackage.getType() == CloudPackage.Type.BITS) { - errorDetails = ImmutableErrorDetails.builder() - .description(((BitsData) cloudPackage.getData()).getError()) - .build(); - } - return ImmutableUpload.builder() - .status(cloudPackage.getStatus()) - .errorDetails(errorDetails) - .build(); - } - - @Override - public CloudBuild getBuild(UUID buildGuid) { - return fetch(() -> getBuildResource(buildGuid), ImmutableRawCloudBuild::of); - } - - @Override - public List getBuildsForApplication(UUID applicationGuid) { - return fetchList(() -> getBuildResourcesByApplicationGuid(applicationGuid), ImmutableRawCloudBuild::of); - } - - @Override - public List getBuildsForPackage(UUID packageGuid) { - return fetchList(() -> getBuildResourcesByPackageGuid(packageGuid), ImmutableRawCloudBuild::of); - } - - @Override - public CloudBuild createBuild(UUID packageGuid) { - return fetch(() -> createBuildResource(packageGuid), ImmutableRawCloudBuild::of); - } - - @Override - public void bindDropletToApp(UUID dropletGuid, UUID applicationGuid) { - delegate.applicationsV3() - .setCurrentDroplet(SetApplicationCurrentDropletRequest.builder() - .applicationId(applicationGuid.toString()) - .data(Relationship.builder() - .id(dropletGuid.toString()) - .build()) - .build()) - .block(); - } - - @Override - public DropletInfo getCurrentDropletForApplication(UUID applicationGuid) { - GetApplicationCurrentDropletResponse dropletResponse = delegate.applicationsV3() - .getCurrentDroplet(GetApplicationCurrentDropletRequest.builder() - .applicationId( - applicationGuid.toString()) - .build()) - .block(); - if (dropletResponse == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not found", - "Application with guid " + applicationGuid + " does not have a droplet"); - } - return parseDropletInfo(dropletResponse); - } - - private DropletInfo parseDropletInfo(GetApplicationCurrentDropletResponse getApplicationCurrentDropletResponse) { - String packageUrl = getApplicationCurrentDropletResponse.getLinks() - .get(Constants.PACKAGE) - .getHref(); - if (packageUrl.endsWith("/")) { - packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf("/")); - } - String packageGuid = packageUrl.substring(packageUrl.lastIndexOf("/") + 1); - return ImmutableDropletInfo.builder() - .guid(UUID.fromString(getApplicationCurrentDropletResponse.getId())) - .packageGuid(UUID.fromString(packageGuid)) - .build(); - } - - @Override - public List getPackagesForApplication(UUID applicationGuid) { - return fetchList(() -> getPackages(applicationGuid.toString()), ImmutableRawCloudPackage::of); - } - - private Flux getPackages(String applicationGuid) { - IntFunction pageRequestSupplier = page -> ListApplicationPackagesRequest.builder() - .page(page) - .applicationId( - applicationGuid) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .listPackages(pageRequestSupplier.apply(page))); - } - - @Override - public Set getUserRolesBySpaceAndUser(UUID spaceGuid, UUID userGuid) { - Supplier> setFactory = () -> EnumSet.noneOf(UserRole.class); - return fetchFlux(() -> getRoles(spaceGuid, userGuid), ImmutableRawUserRole::of).collect(Collectors.toCollection(setFactory)) - .block(); - } - - public CloudPackage createDockerPackage(UUID applicationGuid, DockerInfo dockerInfo) { - org.cloudfoundry.client.v3.packages.DockerData.Builder dockerDataBuilder = org.cloudfoundry.client.v3.packages.DockerData.builder() - .image( - dockerInfo.getImage()); - if (dockerInfo.getCredentials() != null) { - addNonNullDockerCredentials(dockerInfo.getCredentials(), dockerDataBuilder); - } - CreatePackageRequest packageRequest = CreatePackageRequest.builder() - .type(PackageType.DOCKER) - .data(dockerDataBuilder.build()) - .relationships(buildPackageRelationships(applicationGuid)) - .build(); - CreatePackageResponse packageResponse = delegate.packages() - .create(packageRequest) - .block(); - return getPackage(UUID.fromString(packageResponse.getId())); - } - - @Override - public CloudAsyncJob getAsyncJob(String jobId) { - return fetch(() -> delegate.jobsV3() - .get(GetJobRequest.builder() - .jobId(jobId) - .build()), ImmutableRawCloudAsyncJob::of); - } - - private void addNonNullDockerCredentials(DockerCredentials dockerCredentials, - org.cloudfoundry.client.v3.packages.DockerData.Builder dockerDataBuilder) { - String username = dockerCredentials.getUsername(); - if (username != null) { - dockerDataBuilder.username(username); - } - String password = dockerCredentials.getPassword(); - if (password != null) { - dockerDataBuilder.password(password); - } - } - - private Flux getRoles(UUID spaceGuid, UUID userGuid) { - IntFunction pageRequestSupplier = page -> ListRolesRequest.builder() - .page(page) - .spaceId(spaceGuid.toString()) - .userId(userGuid.toString()) - .types(RoleType.values()) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.rolesV3() - .list(pageRequestSupplier.apply(page))); - } - - private CloudApplication findApplicationByName(String name, boolean required) { - CloudApplication application = findApplicationByName(name); - if (application == null && required) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Application " + name + " not found."); - } - return application; - } - - private CloudApplication findApplicationByName(String name) { - return fetch(() -> getApplicationByName(name), application -> ImmutableRawCloudApplication.builder() - .application(application) - .space(target) - .build()); - } - - private Flux getApplicationResources() { - assertSpaceProvided("get application"); - IntFunction pageRequestSupplier = page -> ListApplicationsRequest.builder() - .spaceId(getTargetSpaceGuid().toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .list(pageRequestSupplier.apply(page))); - } - - private Mono getApplicationByGuid(UUID guid) { - return delegate.applicationsV3() - .get(GetApplicationRequest.builder() - .applicationId(guid.toString()) - .build()); - } - - private Mono getApplicationByName(String name) { - assertSpaceProvided("get application"); - IntFunction pageRequestSupplier = page -> ListApplicationsRequest.builder() - .spaceId(getTargetSpaceGuid().toString()) - .name(name) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .list(pageRequestSupplier.apply(page))) - .singleOrEmpty(); - } - - private CloudServiceInstance findServiceInstanceByName(String name) { - return fetchWithAuxiliaryContent(() -> getServiceInstanceResourceByName(name), this::zipWithAuxiliaryServiceInstanceContent); - } - - private Mono getServiceInstanceByGuid(UUID serviceInstanceGuid) { - return delegate.serviceInstancesV3() - .get(GetServiceInstanceRequest.builder() - .serviceInstanceId(serviceInstanceGuid.toString()) - .build()); - } - - private Mono getServiceInstanceResourceByName(String name) { - IntFunction pageRequestSupplier = page -> ListServiceInstancesRequest.builder() - .spaceId( - getTargetSpaceGuid().toString()) - .serviceInstanceName(name) - .page(page) - .build(); - return getServiceInstanceResources(pageRequestSupplier).singleOrEmpty(); - } - - private Flux getServiceInstanceResources(IntFunction pageRequestSupplier) { - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceInstancesV3() - .list(pageRequestSupplier.apply(page))); - } - - private Mono> zipWithAuxiliaryServiceInstanceContent(ServiceInstanceResource serviceInstanceResource) { - if (isUserProvided(serviceInstanceResource)) { - return Mono.just(ImmutableRawCloudServiceInstance.of(serviceInstanceResource)); - } - String servicePlanGuid = serviceInstanceResource.getRelationships() - .getServicePlan() - .getData() - .getId(); - - return getServicePlanResource(servicePlanGuid, serviceInstanceResource.getName()).zipWhen(servicePlan -> getServiceOffering( - servicePlan.getRelationships() - .getServiceOffering() - .getData() - .getId())) - .map( - tuple -> ImmutableRawCloudServiceInstance.builder() - .resource( - serviceInstanceResource) - .servicePlan( - tuple.getT1()) - .serviceOffering( - tuple.getT2()) - .build()); - } - - private boolean isUserProvided(ServiceInstanceResource serviceInstanceResource) { - return ServiceInstanceType.USER_PROVIDED.equals(serviceInstanceResource.getType()); - } - - private Flux getServiceBindingResourcesByServiceInstanceGuid(UUID serviceInstanceGuid) { - IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() - .serviceInstanceId( - serviceInstanceGuid.toString()) - .type( - ServiceBindingType.APPLICATION) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceBindingsV3() - .list(pageRequestSupplier.apply(page))); - } - - private Flux getServiceBindingResourcesByApplicationGuidAndServiceInstanceGuid(UUID applicationGuid, - UUID serviceInstanceGuid) { - IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() - .applicationId( - applicationGuid.toString()) - .serviceInstanceId( - serviceInstanceGuid.toString()) - .page(page) - .build(); - return getApplicationServiceBindingResources(pageRequestSupplier); - } - - private Flux getServiceBindingResourcesByApplicationGuid(UUID applicationGuid) { - IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() - .applicationId( - applicationGuid.toString()) - .page(page) - .build(); - return getApplicationServiceBindingResources(pageRequestSupplier); - } - - private Flux getApplicationServiceBindingResources( - IntFunction pageRequestSupplier) { - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceBindingsV3() - .list(pageRequestSupplier.apply(page))); - } - - private List findServicePlansByBrokerGuid(UUID brokerGuid) { - List offerings = findServiceOfferingsByBrokerGuid(brokerGuid); - return getPlans(offerings); - } - - private List getPlans(List offerings) { - return offerings.stream() - .map(CloudServiceOffering::getServicePlans) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } - - private void updateServicePlanVisibility(CloudServicePlan servicePlan, ServicePlanVisibility visibility) { - updateServicePlanVisibility(getGuid(servicePlan), visibility); - } - - private void updateServicePlanVisibility(UUID servicePlanGuid, ServicePlanVisibility visibility) { - delegate.servicePlansV3() - .updateVisibility(UpdateServicePlanVisibilityRequest.builder() - .servicePlanId(servicePlanGuid.toString()) - .type(Visibility.from(visibility.toString())) - .build()) - .block(); - } - - private Mono getTaskResource(UUID guid) { - GetTaskRequest request = GetTaskRequest.builder() - .taskId(guid.toString()) - .build(); - return delegate.tasks() - .get(request); - } - - private Flux getTaskResourcesByApplicationGuid(UUID applicationGuid) { - IntFunction pageRequestSupplier = page -> ListTasksRequest.builder() - .applicationId(applicationGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.tasks() - .list(pageRequestSupplier.apply(page))); - } - - private CloudTask createTask(UUID applicationGuid, CloudTask task) { - return fetch(() -> createTaskResource(applicationGuid, task), ImmutableRawCloudTask::of); - } - - private Mono createTaskResource(UUID applicationGuid, CloudTask task) { - CreateTaskRequest request = CreateTaskRequest.builder() - .applicationId(applicationGuid.toString()) - .command(task.getCommand()) - .name(task.getName()) - .memoryInMb(task.getLimits() - .getMemory()) - .diskInMb(task.getLimits() - .getDisk()) - .build(); - return delegate.tasks() - .create(request); - } - - private Mono cancelTaskResource(UUID taskGuid) { - CancelTaskRequest request = CancelTaskRequest.builder() - .taskId(taskGuid.toString()) - .build(); - return delegate.tasks() - .cancel(request); - } - - private CloudPackage startUpload(String applicationName, Path file, Duration uploadTimeout) { - Assert.notNull(applicationName, "AppName must not be null"); - Assert.notNull(file, "File must not be null"); - - UUID applicationGuid = getRequiredApplicationGuid(applicationName); - UUID packageGuid = getGuid(createPackageForApplication(applicationGuid)); - - delegate.packages() - .upload(UploadPackageRequest.builder() - .bits(file) - .packageId(packageGuid.toString()) - .build()) - .timeout(uploadTimeout) - .block(); - - return getPackage(packageGuid); - } - - private CloudPackage createPackageForApplication(UUID applicationGuid) { - return fetch(() -> createPackageResource(applicationGuid), ImmutableRawCloudPackage::of); - } - - private Mono createPackageResource(UUID applicationGuid) { - CreatePackageRequest request = CreatePackageRequest.builder() - .type(PackageType.BITS) - .relationships(buildPackageRelationships(applicationGuid)) - .build(); - return delegate.packages() - .create(request); - } - - private PackageRelationships buildPackageRelationships(UUID applicationGuid) { - return PackageRelationships.builder() - .application(buildToOneRelationship(applicationGuid)) - .build(); - } - - private ToOneRelationship buildToOneRelationship(UUID guid) { - return ToOneRelationship.builder() - .data(buildRelationship(guid.toString())) - .build(); - } - - private ToOneRelationship buildToOneRelationship(String guid) { - return ToOneRelationship.builder() - .data(buildRelationship(guid)) - .build(); - } - - private Relationship buildRelationship(String guid) { - return Relationship.builder() - .id(guid) - .build(); - } - - private Mono createBuildResource(UUID packageGuid) { - CreateBuildRequest request = CreateBuildRequest.builder() - .getPackage(buildRelationship(packageGuid.toString())) - .build(); - return delegate.builds() - .create(request); - } - - private Mono getBuildResource(UUID buildGuid) { - GetBuildRequest request = GetBuildRequest.builder() - .buildId(buildGuid.toString()) - .build(); - return delegate.builds() - .get(request); - } - - private Flux getBuildResourcesByApplicationGuid(UUID applicationGuid) { - IntFunction pageRequestSupplier = page -> ListApplicationBuildsRequest.builder() - .applicationId( - applicationGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .listBuilds(pageRequestSupplier.apply(page))); - } - - private Flux getBuildResourcesByPackageGuid(UUID packageGuid) { - IntFunction pageRequestSupplier = page -> ListBuildsRequest.builder() - .packageId(packageGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.builds() - .list(pageRequestSupplier.apply(page))); - } - - private void assertSpaceProvided(String operation) { - Assert.notNull(target, "Unable to " + operation + " without specifying organization and space to use."); - } - - private void removeRoutes(Set routes, UUID applicationGuid) { - for (CloudRoute route : routes) { - for (RouteDestination destination : route.getDestinations()) { - if (destination.getApplicationGuid() - .equals(applicationGuid)) { - unbindRoute(route.getGuid(), destination.getGuid()); - } - } - } - } - - private void validateDomainForRoute(CloudRoute route, Map existingDomains) { - String domain = route.getDomain() - .getName(); - if (!StringUtils.hasLength(domain) || !existingDomains.containsKey(domain)) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), - "Domain '" + domain + "' not found for URI " + route.getUrl()); - } - } - - private void unbindRoute(UUID routeGuid, UUID destinationGuid) { - delegate.routesV3() - .removeDestinations(RemoveRouteDestinationsRequest.builder() - .routeId(routeGuid.toString()) - .destinationId(destinationGuid.toString()) - .build()) - .block(); - } - - private void bindRoute(UUID routeGuid, UUID applicationGuid, String protocol) { - delegate.routesV3() - .insertDestinations(InsertRouteDestinationsRequest.builder() - .routeId(routeGuid.toString()) - .destination(createDestination(applicationGuid, protocol)) - .build()) - .block(); - } - - private Destination createDestination(UUID applicationGuid, String protocol) { - return Destination.builder() - .application(org.cloudfoundry.client.v3.routes.Application.builder() - .applicationId(applicationGuid.toString()) - .build()) - .protocol(protocol) - .build(); - } - - private UUID getOrAddRoute(UUID domainGuid, String host, String path) { - UUID routeGuid = getRouteGuid(domainGuid, host, path); - if (routeGuid == null) { - routeGuid = doAddRoute(domainGuid, host, path); - } - return routeGuid; - } - - private UUID doAddRoute(UUID domainGuid, String host, String path) { - assertSpaceProvided("add route"); - CreateRouteResponse response = delegate.routesV3() - .create(CreateRouteRequest.builder() - .host(host) - .path(path) - .relationships(RouteRelationships.builder() - .domain(buildToOneRelationship( - domainGuid)) - .space(buildToOneRelationship( - getTargetSpaceGuid())) - .build()) - .build()) - .block(); - return getGuid(response); - } - - private void doCreateDomain(String name) { - delegate.domainsV3() - .create(CreateDomainRequest.builder() - .name(name) - .relationships(DomainRelationships.builder() - .organization( - buildToOneRelationship(getTargetOrganizationGuid())) - .build()) - .build()) - .block(); - } - - private void doDeleteDomain(UUID guid) { - delegate.domainsV3() - .delete(DeleteDomainRequest.builder() - .domainId(guid.toString()) - .build()) - .flatMap(jobId -> JobV3Util.waitForCompletion(delegate, DELETE_JOB_TIMEOUT, jobId)) - .block(); - } - - private void doDeleteRoute(UUID guid) { - delegate.routesV3() - .delete(DeleteRouteRequest.builder() - .routeId(guid.toString()) - .build()) - .flatMap(jobId -> JobV3Util.waitForCompletion(delegate, DELETE_JOB_TIMEOUT, jobId)) - .block(); - } - - private void doDeleteServiceInstance(UUID serviceInstanceGuid) { - delegate.serviceInstancesV3() - .delete(DeleteServiceInstanceRequest.builder() - .serviceInstanceId(serviceInstanceGuid.toString()) - .build()) - .block(); - } - - private List doUnbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { - List serviceBindingGuids = getServiceBindingGuids(applicationGuid, serviceInstanceGuid); - if (serviceBindingGuids.isEmpty()) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", - "Service binding between service with GUID " + serviceInstanceGuid - + " and application with GUID " + applicationGuid + " not found."); - } - return doDeleteServiceBindings(serviceBindingGuids); - } - - private List doDeleteServiceBindings(List guids) { - List jobIds = new ArrayList<>(); - List errors = new ArrayList<>(); - for (UUID guid : guids) { - try { - Optional.ofNullable(deleteSingleServiceBinding(guid)) - .ifPresent(jobIds::add); - } catch (AbstractCloudFoundryException e) { - errors.add(e); - } - } - throwOnErrors(errors); - return jobIds; - } - - private String deleteSingleServiceBinding(UUID guid) { - return delegate.serviceBindingsV3() - .delete(DeleteServiceBindingRequest.builder() - .serviceBindingId(guid.toString()) - .build()) - .block(); - } - - private void throwOnErrors(List errors) { - if (errors.isEmpty()) { - return; - } - RuntimeException first = errors.get(0); - errors.subList(1, errors.size()) - .forEach(first::addSuppressed); - throw first; - } - - private CloudDomain findDomainByName(String name, boolean required) { - CloudDomain domain = findDomainByName(name); - if (domain == null && required) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Domain " + name + " not found."); - } - return domain; - } - - private CloudDomain findDomainByName(String name) { - return fetch(() -> getDomainResourceByName(name), ImmutableRawCloudDomain::of); - } - - private List findDomainsByOrganizationGuid(UUID organizationGuid) { - return fetchList(() -> getDomainResourcesByOrganizationGuid(organizationGuid), ImmutableRawCloudDomain::of); - } - - private Mono getDomainResourceByName(String name) { - IntFunction pageRequestSupplier = page -> ListDomainsRequest.builder() - .name(name) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.domainsV3() - .list(pageRequestSupplier.apply(page))) - .singleOrEmpty(); - } - - private Flux getDomainResources() { - IntFunction pageRequestSupplier = page -> ListDomainsRequest.builder() - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.domainsV3() - .list(pageRequestSupplier.apply(page))); - } - - private Flux getSharedDomainResources() { - return getDomainResources().filter(domain -> domain.getRelationships() - .getOrganization() - .getData() == null); - } - - private Flux getPrivateDomainResources() { - return getDomainResources().filter(domain -> domain.getRelationships() - .getOrganization() - .getData() != null); - } - - private Flux getDomainResourcesByOrganizationGuid(UUID organizationGuid) { - IntFunction pageRequestSupplier = page -> ListOrganizationDomainsRequest.builder() - .organizationId( - organizationGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.organizationsV3() - .listDomains(pageRequestSupplier.apply(page))); - } - - private Flux getDomainResourcesByNamesInBatches(Set names) { - return Flux.fromIterable(toBatches(names, MAX_CHAR_LENGTH_FOR_PARAMS_IN_REQUEST)) - .flatMap(this::getDomainResourcesByNames); - } - - private Flux getDomainResourcesByNames(Collection names) { - if (names.isEmpty()) { - return Flux.empty(); - } - IntFunction pageRequestSupplier = page -> ListDomainsRequest.builder() - .names(names) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.domainsV3() - .list(pageRequestSupplier.apply(page))); - } - - private List findRoutes(CloudDomain domain) { - return fetchList(() -> getRouteResourcesByDomainGuidAndSpaceGuid(domain.getGuid(), getTargetSpaceGuid()), - ImmutableRawCloudRoute::of); - } - - private Flux getRouteResourcesByDomainGuidAndSpaceGuid(UUID domainGuid, UUID spaceGuid) { - IntFunction pageRequestSupplier = page -> ListRoutesRequest.builder() - .domainId(domainGuid.toString()) - .spaceId(spaceGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.routesV3() - .list(pageRequestSupplier.apply(page))); - } - - private Flux getRouteResourcesByDomainGuidHostAndPath(UUID domainGuid, String host, String path) { - ListRoutesRequest.Builder requestBuilder = ListRoutesRequest.builder(); - if (host != null) { - requestBuilder.host(host); - } - if (path != null) { - requestBuilder.path(path); - } - requestBuilder.spaceId(getTargetSpaceGuid().toString()) - .domainId(domainGuid.toString()); - - return PaginationUtils.requestClientV3Resources(page -> delegate.routesV3() - .list(requestBuilder.page(page) - .build())); - } - - private Flux getRouteResourcesByAppGuid(UUID applicationGuid) { - IntFunction pageSupplier = page -> ListApplicationRoutesRequest.builder() - .applicationId( - applicationGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.applicationsV3() - .listRoutes(pageSupplier.apply(page))); - } - - private List findServiceOfferingsByBrokerGuid(UUID brokerGuid) { - return fetchListWithAuxiliaryContent(() -> getServiceResourcesByBrokerGuid(brokerGuid), - this::zipWithAuxiliaryServiceOfferingContent); - } - - private List findServiceOfferingsByLabel(String label) { - Assert.notNull(label, "Service label must not be null"); - return fetchListWithAuxiliaryContent(() -> getServiceResourcesByLabel(label), this::zipWithAuxiliaryServiceOfferingContent); - } - - private List findServiceOfferingsByLabelAndBrokerName(String label, String brokerName) { - Assert.notNull(label, "Service label must not be null"); - Assert.notNull(brokerName, "Service broker must not be null"); - return fetchListWithAuxiliaryContent(() -> getServiceResourcesByLabelAndBrokerName(label, brokerName), - this::zipWithAuxiliaryServiceOfferingContent); - } - - private Flux getServiceResources() { - IntFunction pageRequestSupplier = page -> ListServiceOfferingsRequest.builder() - .spaceId( - getTargetSpaceGuid().toString()) - .page(page) - .build(); - return getServiceResources(pageRequestSupplier); - } - - protected Mono getServiceOffering(String offeringId) { - GetServiceOfferingRequest request = GetServiceOfferingRequest.builder() - .serviceOfferingId(offeringId) - .build(); - return delegate.serviceOfferingsV3() - .get(request) - .onErrorMap(t -> doesErrorMatchStatusCode(t, HttpStatus.FORBIDDEN), - t -> new CloudOperationException(HttpStatus.FORBIDDEN, HttpStatus.FORBIDDEN.getReasonPhrase(), - MessageFormat.format( - Messages.SERVICE_OFFERING_WITH_GUID_0_IS_NOT_AVAILABLE, offeringId), - t)) - .onErrorMap(t -> doesErrorMatchStatusCode(t, HttpStatus.NOT_FOUND), - t -> new CloudOperationException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), - MessageFormat.format(Messages.SERVICE_OFFERING_WITH_GUID_0_NOT_FOUND, - offeringId), t)); - } - - private Flux getServiceResourcesByBrokerGuid(UUID brokerGuid) { - IntFunction pageRequestSupplier = page -> ListServiceOfferingsRequest.builder() - .serviceBrokerId( - brokerGuid.toString()) - .spaceId( - getTargetSpaceGuid().toString()) - .page(page) - .build(); - return getServiceResources(pageRequestSupplier); - } - - private Flux getServiceResourcesByLabel(String label) { - IntFunction pageRequestSupplier = page -> ListServiceOfferingsRequest.builder() - .name(label) - .spaceId( - getTargetSpaceGuid().toString()) - .page(page) - .build(); - return getServiceResources(pageRequestSupplier); - } - - private Flux getServiceResourcesByLabelAndBrokerName(String label, String brokerName) { - IntFunction pageRequestSupplier = page -> ListServiceOfferingsRequest.builder() - .name(label) - .serviceBrokerName(brokerName) - .spaceId( - getTargetSpaceGuid().toString()) - .page(page) - .build(); - return getServiceResources(pageRequestSupplier); - } - - private Flux getServiceResources(IntFunction pageRequestSupplier) { - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceOfferingsV3() - .list(pageRequestSupplier.apply(page))); - } - - private Mono> zipWithAuxiliaryServiceOfferingContent(ServiceOfferingResource serviceOffering) { - UUID serviceOfferingGuid = getGuid(serviceOffering); - return getServicePlansFlux(serviceOfferingGuid).collectList() - .map(servicePlans -> ImmutableRawCloudServiceOffering.builder() - .serviceOffering( - serviceOffering) - .servicePlans(servicePlans) - .build()); - } - - private Flux getServicePlansFlux(UUID serviceOfferingGuid) { - return fetchFlux(() -> getServicePlanResourcesByServiceOfferingGuid(serviceOfferingGuid), ImmutableRawCloudServicePlan::of); - } - - protected Mono getServicePlanResource(String servicePlanGuid, String serviceInstanceName) { - GetServicePlanRequest request = GetServicePlanRequest.builder() - .servicePlanId(servicePlanGuid) - .build(); - - return delegate.servicePlansV3() - .get(request) - .onErrorMap(t -> doesErrorMatchStatusCode(t, HttpStatus.FORBIDDEN), - t -> new CloudOperationException(HttpStatus.FORBIDDEN, HttpStatus.FORBIDDEN.getReasonPhrase(), - MessageFormat.format( - Messages.SERVICE_PLAN_WITH_GUID_0_NOT_AVAILABLE_FOR_SERVICE_INSTANCE_1, - servicePlanGuid, serviceInstanceName), t)) - .onErrorMap(t -> doesErrorMatchStatusCode(t, HttpStatus.NOT_FOUND), - t -> new CloudOperationException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), - MessageFormat.format(Messages.NO_SERVICE_PLAN_FOUND, servicePlanGuid, - serviceInstanceName), t)); - } - - private Flux getServicePlanResourcesByServiceOfferingGuid(UUID serviceOfferingGuid) { - IntFunction pageRequestSupplier = page -> ListServicePlansRequest.builder() - .serviceOfferingId( - serviceOfferingGuid.toString()) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.servicePlansV3() - .list(pageRequestSupplier.apply(page))); - } - - private Flux getServiceKeyResource(CloudServiceInstance serviceInstance) { - UUID serviceInstanceGuid = getGuid(serviceInstance); - return getServiceKeyResourcesByServiceInstanceGuid(serviceInstanceGuid); - } - - private Flux getServiceKeyResourcesByServiceInstanceGuid(UUID serviceInstanceGuid) { - IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() - .serviceInstanceId( - serviceInstanceGuid.toString()) - .type(ServiceBindingType.KEY) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceBindingsV3() - .list(pageRequestSupplier.apply(page))); - } - - private Mono getServiceKeyResourceByNameAndServiceInstanceGuid(String name, UUID guid) { - IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() - .serviceInstanceId(guid.toString()) - .type(ServiceBindingType.KEY) - .name(name) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceBindingsV3() - .list(pageRequestSupplier.apply(page))) - .singleOrEmpty(); - } - - private CloudStack findStackResource(String name) { - return fetch(() -> getStackResourceByName(name), ImmutableRawCloudStack::of); - } - - private Flux getStackResources() { - IntFunction pageRequestSupplier = page -> ListStacksRequest.builder() - .page(page) - .build(); - return getStackResources(pageRequestSupplier); - } - - private Mono getStackResourceByName(String name) { - IntFunction pageRequestSupplier = page -> ListStacksRequest.builder() - .name(name) - .page(page) - .build(); - return getStackResources(pageRequestSupplier).singleOrEmpty(); - } - - private Flux getStackResources(IntFunction requestForPage) { - return PaginationUtils.requestClientV3Resources(page -> delegate.stacksV3() - .list(requestForPage.apply(page))); - } - - private List findEventsByTarget(String target) { - return fetchList(() -> getEventResourcesByTarget(target), ImmutableRawCloudEvent::of); - } - - private Flux getEventResources() { - IntFunction pageRequestSupplier = page -> ListAuditEventsRequest.builder() - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.auditEventsV3() - .list(pageRequestSupplier.apply(page))); - } - - private Flux getEventResourcesByTarget(String target) { - IntFunction pageRequestSupplier = page -> ListAuditEventsRequest.builder() - .targetId(target) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.auditEventsV3() - .list(pageRequestSupplier.apply(page))); - } - - private InstancesInfo findApplicationInstances(UUID applicationGuid) { - return fetch(() -> getApplicationProcessStatsResource(applicationGuid), ImmutableRawInstancesInfo::of); - } - - private Mono getApplicationProcessStatsResource(UUID applicationGuid) { - return delegate.applicationsV3() - .getProcessStatistics(GetApplicationProcessStatisticsRequest.builder() - .applicationId(applicationGuid.toString()) - .type("web") - .build()); - } - - private CloudServiceBroker findServiceBrokerByName(String name) { - return fetch(() -> getServiceBrokerResourceByName(name), ImmutableRawCloudServiceBroker::of); - } - - private Flux getServiceBrokerResources() { - IntFunction pageRequestSupplier = page -> ListServiceBrokersRequest.builder() - .page(page) - .build(); - return getServiceBrokerResources(pageRequestSupplier); - } - - private Mono getServiceBrokerResourceByName(String name) { - IntFunction pageRequestSupplier = page -> ListServiceBrokersRequest.builder() - .page(page) - .name(name) - .build(); - return getServiceBrokerResources(pageRequestSupplier).singleOrEmpty(); - } - - private Flux getServiceBrokerResources(IntFunction pageRequestSupplier) { - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceBrokersV3() - .list(pageRequestSupplier.apply(page))); - } - - private CloudServicePlan findPlanForService(CloudServiceInstance service, String planName) { - List offerings = getServiceOfferings(service); - for (var offering : offerings) { - for (var plan : offering.getServicePlans()) { - if (planName.equals(plan.getName())) { - return plan; - } - } - } - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service plan " + planName + " not found."); - } - - private List getServiceOfferings(CloudServiceInstance service) { - if (service.getBroker() == null) { - return findServiceOfferingsByLabel(service.getLabel()); - } - return findServiceOfferingsByLabelAndBrokerName(service.getLabel(), service.getBroker()); - } - - private UUID getRequiredApplicationGuid(String name) { - Resource applicationResource = getApplicationByName(name).block(); - if (applicationResource == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Application " + name + " not found."); - } - return UUID.fromString(applicationResource.getId()); - } - - private Mono getServiceInstanceByName(String name) { - IntFunction pageRequestSupplier = page -> ListServiceInstancesRequest.builder() - .spaceId( - getTargetSpaceGuid().toString()) - .serviceInstanceName(name) - .page(page) - .build(); - return PaginationUtils.requestClientV3Resources(page -> delegate.serviceInstancesV3() - .list(pageRequestSupplier.apply(page))) - .singleOrEmpty(); - } - - private UUID getRequiredDomainGuid(String name) { - return getGuid(findDomainByName(name, true)); - } - - private Map getDomainsFromRoutes(Set routes) { - Set domainNames = routes.stream() - .map(route -> route.getDomain() - .getName()) - .filter(StringUtils::hasLength) - .collect(Collectors.toSet()); - return getDomainResourcesByNamesInBatches(domainNames).collectMap(DomainResource::getName, - domain -> UUID.fromString(domain.getId())) - .block(); - } - - private UUID getRouteGuid(UUID domainGuid, String host, String path) { - List routeEntitiesResource = getRouteResourcesByDomainGuidHostAndPath(domainGuid, host, path).collect( - Collectors.toList()) - .block(); - if (CollectionUtils.isEmpty(routeEntitiesResource)) { - return null; - } - return getGuid(routeEntitiesResource.get(0)); - } - - private List getServiceBindingGuids(UUID applicationGuid, UUID serviceInstanceGuid) { - return getServiceBindingResourcesByApplicationGuidAndServiceInstanceGuid(applicationGuid, serviceInstanceGuid).map(this::getGuid) - .collectList() - .block(); - } - - private void processAsyncUploadInBackground(CloudPackage cloudPackage, UploadStatusCallback callback) { - String threadName = String.format("App upload monitor: %s", cloudPackage.getGuid()); - new Thread(() -> processAsyncUpload(cloudPackage, callback), threadName).start(); - } - - private void processAsyncUpload(CloudPackage cloudPackage, UploadStatusCallback callback) { - while (true) { - Upload upload = getUploadStatus(cloudPackage.getGuid()); - Status uploadStatus = upload.getStatus(); - boolean unsubscribe = callback.onProgress(uploadStatus.toString()); - if (unsubscribe || isUploadReady(uploadStatus)) { - return; - } - if (hasUploadFailed(uploadStatus)) { - callback.onError(upload.getErrorDetails() - .getDescription()); - return; - } - try { - Thread.sleep(PACKAGE_UPLOAD_JOB_POLLING_PERIOD); - } catch (InterruptedException e) { - return; - } - } - } - - private boolean isUploadReady(Status status) { - return status == Status.READY; - } - - private boolean hasUploadFailed(Status status) { - return status == Status.EXPIRED || status == Status.FAILED; - } - - @Override - public CloudPackage getPackage(UUID packageGuid) { - return fetch(() -> getPackageResource(packageGuid), ImmutableRawCloudPackage::of); - } - - private Mono getPackageResource(UUID guid) { - GetPackageRequest request = GetPackageRequest.builder() - .packageId(guid.toString()) - .build(); - return delegate.packages() - .get(request); - } - - private UUID getTargetOrganizationGuid() { - return getGuid(target.getOrganization()); - } - - private UUID getTargetSpaceGuid() { - return getGuid(target); - } - - private UUID getGuid(CloudEntity entity) { - return Optional.ofNullable(entity) - .map(CloudEntity::getMetadata) - .map(CloudMetadata::getGuid) - .orElse(null); - } - - private UUID getGuid(Resource resource) { - return Optional.ofNullable(resource) - .map(Resource::getId) - .map(UUID::fromString) - .orElse(null); - } - - private boolean doesErrorMatchStatusCode(Throwable t, HttpStatus status) { - if (t instanceof AbstractCloudFoundryException e) { - return e.getStatusCode() == status.value(); - } - return false; - } - - private > Flux fetchFluxWithAuxiliaryContent(Supplier> resourceSupplier, - Function> resourceMapper) { - return resourceSupplier.get() - .flatMap(resourceMapper) - .map(Derivable::derive); - } - - private > List fetchListWithAuxiliaryContent(Supplier> resourceSupplier, - Function> resourceMapper) { - return fetchFluxWithAuxiliaryContent(resourceSupplier, resourceMapper).collectList() - .block(); - } - - private > List fetchList(Supplier> resourceSupplier, Function resourceMapper) { - return fetchFlux(resourceSupplier, resourceMapper).collectList() - .block(); - } - - private > Flux fetchFlux(Supplier> resourceSupplier, Function resourceMapper) { - return resourceSupplier.get() - .map(resourceMapper) - .map(Derivable::derive); - } - - private > T fetchWithAuxiliaryContent(Supplier> resourceSupplier, - Function> resourceMapper) { - return fetchMonoWithAuxiliaryContent(resourceSupplier, resourceMapper).block(); - } - - private > T fetch(Supplier> resourceSupplier, Function resourceMapper) { - return fetchMono(resourceSupplier, resourceMapper).block(); - } - - private > Mono fetchMonoWithAuxiliaryContent(Supplier> resourceSupplier, - Function> resourceMapper) { - return resourceSupplier.get() - .flatMap(resourceMapper) - .map(Derivable::derive); - } - - private > Mono fetchMono(Supplier> resourceSupplier, Function resourceMapper) { - return resourceSupplier.get() - .map(resourceMapper) - .map(Derivable::derive); - } - -} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientV3Impl.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientV3Impl.java new file mode 100644 index 0000000000..031d2c4960 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientV3Impl.java @@ -0,0 +1,654 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.nio.file.Path; +import java.text.MessageFormat; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; + +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.UploadStatusCallback; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudPackage; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudRoute; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBroker; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceOffering; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudStack; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudTask; +import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Upload; +import org.cloudfoundry.multiapps.controller.client.facade.domain.UserRole; +import org.cloudfoundry.multiapps.controller.client.facade.dto.ApplicationToCreateDto; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.RestClient; + +public class CloudControllerRestClientV3Impl implements CloudControllerRestClient { + + private final CloudSpace target; + + private final ApplicationsV3Operations applicationsOperations; + private final BuildsV3Operations buildsOperations; + private final DomainsV3Operations domainsOperations; + private final EventsV3Operations eventsOperations; + private final JobsV3Operations jobsOperations; + private final PackagesV3Operations packagesOperations; + private final ProcessesV3Operations processesOperations; + private final RolesV3Operations rolesOperations; + private final RoutesV3Operations routesOperations; + private final ServiceBindingsV3Operations serviceBindingsOperations; + private final ServiceBrokersV3Operations serviceBrokersOperations; + private final ServiceInstancesV3Operations serviceInstancesOperations; + private final ServiceKeysV3Operations serviceKeysOperations; + private final ServiceOfferingsV3Operations serviceOfferingsOperations; + private final StacksV3Operations stacksOperations; + private final TasksV3Operations tasksOperations; + + public CloudControllerRestClientV3Impl(CloudSpace target, CloudControllerV3Client cloudControllerClient, + Function uploadRestClientFactory) { + this.target = target; + this.applicationsOperations = new ApplicationsV3Operations(cloudControllerClient, target); + this.buildsOperations = new BuildsV3Operations(cloudControllerClient); + this.domainsOperations = new DomainsV3Operations(cloudControllerClient, target); + this.eventsOperations = new EventsV3Operations(cloudControllerClient, target); + this.jobsOperations = new JobsV3Operations(cloudControllerClient); + this.packagesOperations = new PackagesV3Operations(cloudControllerClient, target, uploadRestClientFactory); + this.processesOperations = new ProcessesV3Operations(cloudControllerClient, target); + this.rolesOperations = new RolesV3Operations(cloudControllerClient); + this.routesOperations = new RoutesV3Operations(cloudControllerClient, target); + this.serviceBindingsOperations = new ServiceBindingsV3Operations(cloudControllerClient, target); + this.serviceBrokersOperations = new ServiceBrokersV3Operations(cloudControllerClient, target); + this.serviceInstancesOperations = new ServiceInstancesV3Operations(cloudControllerClient, target); + this.serviceKeysOperations = new ServiceKeysV3Operations(cloudControllerClient); + this.serviceOfferingsOperations = new ServiceOfferingsV3Operations(cloudControllerClient, target); + this.stacksOperations = new StacksV3Operations(cloudControllerClient); + this.tasksOperations = new TasksV3Operations(cloudControllerClient, target); + } + + @Override + public CloudSpace getTarget() { + return target; + } + + @Override + public void addDomain(String domainName) { + domainsOperations.addDomain(domainName); + } + + @Override + public void addRoute(String host, String domainName, String path) { + routesOperations.addRoute(host, domainName, path); + } + + @Override + public Optional bindServiceInstance(String bindingName, String applicationName, String serviceInstanceName) { + return serviceBindingsOperations.bindServiceInstance(bindingName, applicationName, serviceInstanceName); + } + + @Override + public Optional bindServiceInstance(String bindingName, String applicationName, String serviceInstanceName, + Map parameters) { + return serviceBindingsOperations.bindServiceInstance(bindingName, applicationName, serviceInstanceName, parameters); + } + + @Override + public void createApplication(ApplicationToCreateDto dto) { + applicationsOperations.createApplication(dto); + + if (dto.getStaging() != null) { + updateApplicationStaging(dto.getName(), dto.getStaging()); + } + + if (dto.getRoutes() != null && !dto.getRoutes() + .isEmpty()) { + updateApplicationRoutes(dto.getName(), dto.getRoutes()); + } + } + + @Override + public void createServiceInstance(CloudServiceInstance serviceInstance) { + serviceInstancesOperations.createServiceInstance(serviceInstance); + } + + @Override + public String createServiceBroker(CloudServiceBroker serviceBroker) { + return serviceBrokersOperations.createServiceBroker(serviceBroker); + } + + @Override + public CloudServiceKey createAndFetchServiceKey(CloudServiceKey keyModel, String serviceInstanceName) { + return serviceKeysOperations.createAndFetchServiceKey(keyModel, getServiceInstance(serviceInstanceName)); + } + + @Override + public Optional createServiceKey(CloudServiceKey keyModel, String serviceInstanceName) { + return serviceKeysOperations.createServiceKey(keyModel, getServiceInstance(serviceInstanceName)); + } + + @Override + public Optional createServiceKey(String serviceInstanceName, String serviceKeyName, Map parameters) { + return serviceKeysOperations.createServiceKey(getServiceInstance(serviceInstanceName), serviceKeyName, parameters); + } + + @Override + public void createUserProvidedServiceInstance(CloudServiceInstance serviceInstance) { + serviceInstancesOperations.createUserProvidedServiceInstance(serviceInstance); + } + + @Override + public void deleteApplication(String applicationName) { + applicationsOperations.deleteApplication(applicationName); + } + + @Override + public void deleteDomain(String domainName) { + domainsOperations.deleteDomain(domainName); + } + + @Override + public void deleteOrphanedRoutes() { + routesOperations.deleteOrphanedRoutes(); + } + + @Override + public void deleteRoute(String host, String domainName, String path) { + routesOperations.deleteRoute(host, domainName, path); + } + + @Override + public void deleteServiceInstance(String serviceInstanceName) { + serviceInstancesOperations.deleteServiceInstance(serviceInstanceName); + } + + @Override + public void deleteServiceInstance(CloudServiceInstance serviceInstance) { + serviceInstancesOperations.deleteServiceInstance(serviceInstance); + } + + @Override + public String deleteServiceBroker(String name) { + return serviceBrokersOperations.deleteServiceBroker(name); + } + + @Override + public Optional deleteServiceBinding(String serviceInstanceName, String serviceKeyName) { + CloudServiceKey serviceKey = getServiceKey(serviceInstanceName, serviceKeyName); + + if (serviceKey == null) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_KEY_0_NOT_FOUND, serviceKeyName)); + } + + return deleteServiceBinding(serviceKey.getGuid()); + } + + @Override + public Optional deleteServiceBinding(UUID bindingGuid) { + return serviceBindingsOperations.deleteServiceBinding(bindingGuid); + } + + @Override + public CloudApplication getApplication(String applicationName) { + return applicationsOperations.getApplication(applicationName); + } + + @Override + public CloudApplication getApplication(String applicationName, boolean required) { + return applicationsOperations.getApplication(applicationName, required); + } + + @Override + public UUID getApplicationGuid(String applicationName) { + return applicationsOperations.getApplicationGuid(applicationName); + } + + @Override + public String getApplicationName(UUID applicationGuid) { + return applicationsOperations.getApplicationName(applicationGuid); + } + + @Override + public Map getApplicationEnvironment(UUID applicationGuid) { + return applicationsOperations.getApplicationEnvironment(applicationGuid); + } + + @Override + public Map getApplicationEnvironment(String applicationName) { + return applicationsOperations.getApplicationEnvironment(applicationName); + } + + @Override + public List getApplications() { + return applicationsOperations.getApplications(); + } + + @Override + public List getApplicationsByMetadataLabelSelector(String labelSelector) { + return applicationsOperations.getApplicationsByMetadataLabelSelector(labelSelector); + } + + @Override + public List getApplicationEvents(String applicationName) { + return eventsOperations.getApplicationEvents(applicationName); + } + + @Override + public List getEventsByTarget(UUID uuid) { + return eventsOperations.getEventsByTarget(uuid); + } + + @Override + public InstancesInfo getApplicationInstances(CloudApplication app) { + return processesOperations.getApplicationInstances(app); + } + + @Override + public InstancesInfo getApplicationInstances(UUID applicationGuid) { + return processesOperations.getApplicationInstances(applicationGuid); + } + + @Override + public CloudProcess getApplicationProcess(UUID applicationGuid) { + return processesOperations.getApplicationProcess(applicationGuid); + } + + @Override + public List getApplicationRoutes(UUID applicationGuid) { + return routesOperations.getApplicationRoutes(applicationGuid); + } + + @Override + public boolean getApplicationSshEnabled(UUID applicationGuid) { + return processesOperations.getApplicationSshEnabled(applicationGuid); + } + + @Override + public Map getApplicationFeatures(UUID applicationGuid) { + return processesOperations.getApplicationFeatures(applicationGuid); + } + + @Override + public CloudDomain getDefaultDomain() { + return domainsOperations.getDefaultDomain(); + } + + @Override + public List getDomains() { + return domainsOperations.getDomains(); + } + + @Override + public List getDomainsForOrganization() { + return domainsOperations.getDomainsForOrganization(); + } + + @Override + public List getEvents() { + return eventsOperations.getEvents(); + } + + @Override + public List getPrivateDomains() { + return domainsOperations.getPrivateDomains(); + } + + @Override + public List getRoutes(String domainName) { + return routesOperations.getRoutes(domainName); + } + + @Override + public UUID getRequiredServiceInstanceGuid(String name) { + return serviceInstancesOperations.getRequiredServiceInstanceGuid(name); + } + + @Override + public CloudServiceInstance getServiceInstance(String serviceInstanceName) { + return serviceInstancesOperations.getServiceInstance(serviceInstanceName); + } + + @Override + public CloudServiceInstance getServiceInstance(String serviceInstanceName, boolean required) { + return serviceInstancesOperations.getServiceInstance(serviceInstanceName, required); + } + + @Override + public String getServiceInstanceName(UUID serviceInstanceGuid) { + return serviceInstancesOperations.getServiceInstanceName(serviceInstanceGuid); + } + + @Override + public CloudServiceInstance getServiceInstanceWithoutAuxiliaryContent(String serviceInstanceName) { + return serviceInstancesOperations.getServiceInstanceWithoutAuxiliaryContent(serviceInstanceName); + } + + @Override + public CloudServiceInstance getServiceInstanceWithoutAuxiliaryContent(String serviceInstanceName, boolean required) { + return serviceInstancesOperations.getServiceInstanceWithoutAuxiliaryContent(serviceInstanceName, required); + } + + @Override + public CloudServiceBinding getServiceBinding(UUID serviceBindingGuid) { + return serviceBindingsOperations.getServiceBinding(serviceBindingGuid); + } + + @Override + public List getServiceAppBindings(UUID serviceInstanceGuid) { + return serviceBindingsOperations.getServiceAppBindings(serviceInstanceGuid); + } + + @Override + public List getAppBindings(UUID applicationGuid) { + return serviceBindingsOperations.getAppBindings(applicationGuid); + } + + @Override + public List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid) { + return serviceBindingsOperations.getServiceBindingsForApplication(applicationId, serviceInstanceGuid); + } + + @Override + public CloudServiceBroker getServiceBroker(String name) { + return serviceBrokersOperations.getServiceBroker(name); + } + + @Override + public CloudServiceBroker getServiceBroker(String name, boolean required) { + return serviceBrokersOperations.getServiceBroker(name, required); + } + + @Override + public List getServiceBrokers() { + return serviceBrokersOperations.getServiceBrokers(); + } + + @Override + public CloudServiceKey getServiceKey(String serviceInstanceName, String serviceKeyName) { + return serviceKeysOperations.getServiceKey(getServiceInstance(serviceInstanceName), serviceKeyName); + } + + @Override + public List getServiceKeys(String serviceInstanceName) { + return serviceKeysOperations.getServiceKeys(getServiceInstance(serviceInstanceName)); + } + + @Override + public List getServiceKeysWithCredentials(String serviceInstanceName) { + return serviceKeysOperations.getServiceKeysWithCredentials(getServiceInstance(serviceInstanceName)); + } + + @Override + public List getServiceKeys(CloudServiceInstance serviceInstance) { + return serviceKeysOperations.getServiceKeys(serviceInstance); + } + + @Override + public List getServiceKeysWithCredentials(CloudServiceInstance serviceInstance) { + return serviceKeysOperations.getServiceKeysWithCredentials(serviceInstance); + } + + @Override + public List getServiceOfferings() { + return serviceOfferingsOperations.getServiceOfferings(); + } + + @Override + public List getSharedDomains() { + return domainsOperations.getSharedDomains(); + } + + @Override + public CloudStack getStack(String name) { + return stacksOperations.getStack(name); + } + + @Override + public CloudStack getStack(String name, boolean required) { + return stacksOperations.getStack(name, required); + } + + @Override + public List getStacks() { + return stacksOperations.getStacks(); + } + + @Override + public void startApplication(String applicationName) { + applicationsOperations.startApplication(applicationName); + } + + @Override + public void restartApplication(String applicationName) { + stopApplication(applicationName); + startApplication(applicationName); + } + + @Override + public void stopApplication(String applicationName) { + applicationsOperations.stopApplication(applicationName); + } + + @Override + public void rename(String applicationName, String newName) { + applicationsOperations.rename(applicationName, newName); + } + + @Override + public void updateApplicationInstances(String applicationName, int instances) { + applicationsOperations.updateApplicationInstances(applicationName, instances); + } + + @Override + public void updateApplicationMemory(String applicationName, int memory) { + applicationsOperations.updateApplicationMemory(applicationName, memory); + } + + @Override + public void updateApplicationDiskQuota(String applicationName, int disk) { + applicationsOperations.updateApplicationDiskQuota(applicationName, disk); + } + + @Override + public void updateApplicationEnv(String applicationName, Map env) { + applicationsOperations.updateApplicationEnv(applicationName, env); + } + + @Override + public void bindDropletToApp(UUID dropletGuid, UUID applicationGuid) { + applicationsOperations.bindDropletToApp(dropletGuid, applicationGuid); + } + + @Override + public List unbindServiceInstance(String applicationName, String serviceInstanceName) { + return serviceBindingsOperations.unbindServiceInstance(applicationName, serviceInstanceName); + } + + @Override + public List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { + return serviceBindingsOperations.unbindServiceInstance(applicationGuid, serviceInstanceGuid); + } + + @Override + public void updateApplicationStaging(String applicationName, Staging staging) { + processesOperations.updateApplicationStaging(applicationName, staging); + } + + @Override + public void updateApplicationRoutes(String applicationName, Set routes) { + routesOperations.updateApplicationRoutes(applicationName, routes); + } + + @Override + public String updateServiceBroker(CloudServiceBroker serviceBroker) { + return serviceBrokersOperations.updateServiceBroker(serviceBroker); + } + + @Override + public void updateServicePlanVisibilityForBroker(String name, ServicePlanVisibility visibility) { + serviceBrokersOperations.updateServicePlanVisibilityForBroker(name, visibility); + } + + @Override + public void updateServicePlan(String serviceName, String planName) { + serviceInstancesOperations.updateServicePlan(serviceName, planName); + } + + @Override + public void updateServiceParameters(String serviceName, Map parameters) { + serviceInstancesOperations.updateServiceParameters(serviceName, parameters); + } + + @Override + public void updateServiceSyslogDrainUrl(String serviceName, String syslogDrainUrl) { + serviceInstancesOperations.updateServiceSyslogDrainUrl(serviceName, syslogDrainUrl); + } + + @Override + public void updateServiceTags(String serviceName, List tags) { + serviceInstancesOperations.updateServiceTags(serviceName, tags); + } + + @Override + public CloudPackage asyncUploadApplication(String applicationName, Path file, UploadStatusCallback callback, Duration uploadTimeout) { + return packagesOperations.asyncUploadApplication(applicationName, file, callback, uploadTimeout); + } + + @Override + public Upload getUploadStatus(UUID packageGuid) { + return packagesOperations.getUploadStatus(packageGuid); + } + + @Override + public CloudTask getTask(UUID taskGuid) { + return tasksOperations.getTask(taskGuid); + } + + @Override + public List getTasks(String applicationName) { + return tasksOperations.getTasks(applicationName); + } + + @Override + public CloudTask runTask(String applicationName, CloudTask task) { + return tasksOperations.runTask(applicationName, task); + } + + @Override + public CloudTask cancelTask(UUID taskGuid) { + return tasksOperations.cancelTask(taskGuid); + } + + @Override + public CloudBuild createBuild(UUID packageGuid) { + return buildsOperations.createBuild(packageGuid); + } + + @Override + public CloudBuild getBuild(UUID packageGuid) { + return buildsOperations.getBuild(packageGuid); + } + + @Override + public List getBuildsForApplication(UUID applicationGuid) { + return buildsOperations.getBuildsForApplication(applicationGuid); + } + + @Override + public Map getServiceInstanceParameters(UUID guid) { + return serviceInstancesOperations.getServiceInstanceParameters(guid); + } + + @Override + public Map getUserProvidedServiceInstanceParameters(UUID guid) { + return serviceInstancesOperations.getUserProvidedServiceInstanceParameters(guid); + } + + @Override + public Map getServiceBindingParameters(UUID guid) { + return serviceBindingsOperations.getServiceBindingParameters(guid); + } + + @Override + public List getBuildsForPackage(UUID packageGuid) { + return buildsOperations.getBuildsForPackage(packageGuid); + } + + @Override + public List getServiceInstancesWithoutAuxiliaryContentByNames(List names) { + return serviceInstancesOperations.getServiceInstancesWithoutAuxiliaryContentByNames(names); + } + + @Override + public List getServiceInstancesByMetadataLabelSelector(String labelSelector) { + return serviceInstancesOperations.getServiceInstancesByMetadataLabelSelector(labelSelector); + } + + @Override + public List getServiceInstancesWithoutAuxiliaryContentByMetadataLabelSelector(String labelSelector) { + return serviceInstancesOperations.getServiceInstancesWithoutAuxiliaryContentByMetadataLabelSelector(labelSelector); + } + + @Override + public void updateApplicationMetadata(UUID guid, Metadata metadata) { + applicationsOperations.updateApplicationMetadata(guid, metadata); + } + + @Override + public void updateServiceInstanceMetadata(UUID guid, Metadata metadata) { + serviceInstancesOperations.updateServiceInstanceMetadata(guid, metadata); + } + + @Override + public void updateServiceBindingMetadata(UUID guid, Metadata metadata) { + serviceBindingsOperations.updateServiceBindingMetadata(guid, metadata); + } + + @Override + public DropletInfo getCurrentDropletForApplication(UUID applicationGuid) { + return processesOperations.getCurrentDropletForApplication(applicationGuid); + } + + @Override + public CloudPackage getPackage(UUID packageGuid) { + return packagesOperations.getPackage(packageGuid); + } + + @Override + public List getPackagesForApplication(UUID applicationGuid) { + return packagesOperations.getPackagesForApplication(applicationGuid); + } + + @Override + public Set getUserRolesBySpaceAndUser(UUID spaceGuid, UUID userGuid) { + return rolesOperations.getUserRolesBySpaceAndUser(spaceGuid, userGuid); + } + + @Override + public CloudPackage createDockerPackage(UUID applicationGuid, DockerInfo dockerInfo) { + return packagesOperations.createDockerPackage(applicationGuid, dockerInfo); + } + + @Override + public CloudAsyncJob getAsyncJob(String jobId) { + return jobsOperations.getAsyncJob(jobId); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3Client.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3Client.java new file mode 100644 index 0000000000..7ad492bafa --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3Client.java @@ -0,0 +1,238 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.net.URI; +import java.text.MessageFormat; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudException; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Job; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestClient; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Flux; + +public class CloudControllerV3Client { + + private final RestClient restClient; + + private final WebClient webClient; + + public CloudControllerV3Client(RestClient restClient) { + this(restClient, null); + } + + public CloudControllerV3Client(RestClient restClient, WebClient webClient) { + this.restClient = restClient; + this.webClient = webClient; + } + + public T get(String uri, Class responseType) { + return restClient.get() + .uri(uri) + .retrieve() + .body(responseType); + } + + public Optional getOptional(String uri, Class responseType) { + try { + return Optional.ofNullable(get(uri, responseType)); + } catch (CloudOperationException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } + throw e; + } + } + + public List list(String firstPageUri, ParameterizedTypeReference> pageType) { + String firstPageRelativeUri = toRelativeIfAbsolute(firstPageUri); + + V3ListResponse firstPage = restClient.get() + .uri(firstPageRelativeUri) + .retrieve() + .body(pageType); + + if (firstPage == null) { + return new ArrayList<>(); + } + + Integer totalPages = firstPage.pagination() == null ? null + : firstPage.pagination() + .totalPages(); + + if (webClient == null || totalPages == null || totalPages <= 1) { + return listSequentially(firstPage, pageType); + } + + List resultFromAllPages = new ArrayList<>(firstPage.resources()); + resultFromAllPages.addAll(fetchRemainingPagesConcurrently(firstPageRelativeUri, totalPages, pageType)); + return resultFromAllPages; + } + + private List listSequentially(V3ListResponse firstPage, ParameterizedTypeReference> pageType) { + List resultFromAllPages = new ArrayList<>(firstPage.resources()); + String nextUri = toRelativeIfAbsolute(firstPage.nextPageHref()); + + while (nextUri != null) { + V3ListResponse currentPage = restClient.get() + .uri(nextUri) + .retrieve() + .body(pageType); + + if (currentPage == null) { + break; + } + + resultFromAllPages.addAll(currentPage.resources()); + nextUri = toRelativeIfAbsolute(currentPage.nextPageHref()); + } + + return resultFromAllPages; + } + + private List fetchRemainingPagesConcurrently(String firstPageRelativeUri, int totalPages, + ParameterizedTypeReference> pageType) { + return Flux.range(2, totalPages - 1) + .flatMapSequential(page -> webClient.get() + .uri(pageUri(firstPageRelativeUri, page)) + .retrieve() + .bodyToMono(pageType) + .onErrorMap(WebClientResponseException.class, this::toCloudOperationException), + Constants.MAX_CONCURRENT_PAGES) + .flatMapIterable(V3ListResponse::resources) + .collectList() + .block(); + } + + static String pageUri(String firstPageRelativeUri, int page) { + return UriComponentsBuilder.fromUriString(firstPageRelativeUri) + .replaceQueryParam("page", page) + .build() + .toUriString(); + } + + private CloudOperationException toCloudOperationException(WebClientResponseException e) { + HttpStatus status = HttpStatus.valueOf(e.getStatusCode() + .value()); + + return new CloudOperationException(status, status.getReasonPhrase(), e.getResponseBodyAsString(), e); + } + + public void followAsyncJob(ResponseEntity acceptedResponse, Duration timeout) { + URI location = acceptedResponse.getHeaders() + .getLocation(); + + if (location == null) { + return; // when location is null, the job has already completed and we can return immediately + } + + waitForAsyncJob(extractJobGuid(location.toString()), timeout); + } + + public V3Job waitForAsyncJob(String jobGuid, Duration timeout) { + long startTimeNanos = System.nanoTime(); + long timeoutNanos = timeout.toNanos(); + Duration interval = Constants.JOB_POLL_MIN_INTERVAL; + + while (true) { + V3Job asyncJob = get("/v3/jobs/" + jobGuid, V3Job.class); + + if (asyncJob != null && asyncJob.isTerminal()) { + + if (asyncJob.isFailed()) { + throw jobFailed(asyncJob); + } + + return asyncJob; + } + + long elapsedNanos = System.nanoTime() - startTimeNanos; + if (elapsedNanos >= timeoutNanos) { + throw new CloudOperationException(HttpStatus.GATEWAY_TIMEOUT, Messages.JOB_TIMEOUT, + MessageFormat.format(Messages.JOB_0_DID_NOT_COMPLETE_WITHIN_1, jobGuid, timeout)); + } + + long remainingNanos = timeoutNanos - elapsedNanos; + Duration sleepDuration = interval.toNanos() > remainingNanos ? Duration.ofNanos(remainingNanos) : interval; + + sleep(sleepDuration); + interval = nextInterval(interval); + } + } + + private static Duration nextInterval(Duration current) { + Duration doubled = current.multipliedBy(2); + return doubled.compareTo(Constants.JOB_POLL_MAX_INTERVAL) > 0 ? Constants.JOB_POLL_MAX_INTERVAL : doubled; + } + + private static void sleep(Duration interval) { + try { + Thread.sleep(interval.toMillis()); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + throw new CloudException(MessageFormat.format(Messages.INTERRUPTED_WHILE_POLLING_ASYNC_JOB_0, e.getMessage()), e); + } + } + + private static CloudOperationException jobFailed(V3Job job) { + String detail = "Job failed"; + + if (job.errors() != null && !job.errors() + .isEmpty()) { + detail = job.errors() + .stream() + .map(V3Job.V3Error::detail) + .reduce((a, b) -> a + "\n" + b) + .orElse(detail); + } + + return new CloudOperationException(HttpStatus.UNPROCESSABLE_ENTITY, Messages.JOB_FAILED, detail); + } + + private static String extractJobGuid(String location) { + int index = location.lastIndexOf("/v3/jobs/"); + + if (index < 0) { + return location.substring(location.lastIndexOf('/') + 1); + } + + return location.substring(index + "/v3/jobs/".length()); + } + + private String toRelativeIfAbsolute(String href) { + if (href == null) { + return null; + } + + URI uri = URI.create(href); + + if (uri.isAbsolute()) { + String pathAndQuery = uri.getRawPath(); + + if (uri.getRawQuery() != null) { + pathAndQuery = pathAndQuery + "?" + uri.getRawQuery(); + } + + return pathAndQuery; + } + + return href; + } + + public RestClient getRestClient() { + return restClient; + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3Endpoints.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3Endpoints.java new file mode 100644 index 0000000000..5cbce89a69 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3Endpoints.java @@ -0,0 +1,88 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +/** + * The Cloud Controller v3 API surface used by the in-house CF client — resource roots, {@code {guid}} path templates, and query-parameter + * fragments — in one place, so the {@code V3Operations} classes reference named constants instead of inline "magic string" URLs. + *

+ * Two URI mechanisms are represented, matching how the operations classes issue requests: + *

    + *
  • Path templates (e.g. {@link #APP_BY_GUID}) contain Spring {@code {placeholder}}s expanded by {@code RestClient.uri(template, + * args...)} — the placeholders are intentionally kept literally.
  • + *
  • Roots + query fragments (e.g. {@link #APPS} + {@link #QUERY_PER_PAGE} + {@link #AMPERSAND_SPACE_GUIDS}) are concatenated by the caller + * into a query string. The fragments include their leading {@code ?}/{@code &} and trailing {@code =} exactly as the callers write them, + * so swapping a literal for a constant is a byte-identical substitution.
  • + *
+ */ +public final class CloudControllerV3Endpoints { + + private CloudControllerV3Endpoints() { + } + + public static final int DEFAULT_PAGE_SIZE = 5000; + + public static final String APPS = "/v3/apps"; + public static final String ROUTES = "/v3/routes"; + public static final String DOMAINS = "/v3/domains"; + public static final String SERVICE_INSTANCES = "/v3/service_instances"; + public static final String SERVICE_CREDENTIAL_BINDINGS = "/v3/service_credential_bindings"; + public static final String SERVICE_BROKERS = "/v3/service_brokers"; + public static final String SERVICE_OFFERINGS = "/v3/service_offerings"; + public static final String SERVICE_PLANS = "/v3/service_plans"; + public static final String PACKAGES = "/v3/packages"; + public static final String BUILDS = "/v3/builds"; + public static final String TASKS = "/v3/tasks"; + public static final String PROCESSES = "/v3/processes"; + public static final String STACKS = "/v3/stacks"; + public static final String AUDIT_EVENTS = "/v3/audit_events"; + public static final String JOBS = "/v3/jobs"; + public static final String ROLES = "/v3/roles"; + public static final String ORGANIZATIONS = "/v3/organizations"; + public static final String SPACES = "/v3/spaces"; + + public static final String APP_BY_GUID = "/v3/apps/{guid}"; + public static final String APP_START = "/v3/apps/{guid}/actions/start"; + public static final String APP_STOP = "/v3/apps/{guid}/actions/stop"; + public static final String APP_ENV_VARS = "/v3/apps/{guid}/environment_variables"; + public static final String APP_FEATURE = "/v3/apps/{guid}/features/{name}"; + public static final String APP_WEB_PROCESS_SCALE = "/v3/apps/{guid}/processes/web/actions/scale"; + public static final String APP_CURRENT_DROPLET = "/v3/apps/{guid}/relationships/current_droplet"; + public static final String APP_TASKS = "/v3/apps/{guid}/tasks"; + public static final String ROUTE_BY_GUID = "/v3/routes/{guid}"; + public static final String ROUTE_DESTINATIONS = "/v3/routes/{guid}/destinations"; + public static final String ROUTE_DESTINATION_BY_GUID = "/v3/routes/{routeGuid}/destinations/{destinationGuid}"; + public static final String DOMAIN_BY_GUID = "/v3/domains/{guid}"; + public static final String SERVICE_INSTANCE_BY_GUID = "/v3/service_instances/{guid}"; + public static final String SERVICE_BROKER_BY_GUID = "/v3/service_brokers/{guid}"; + public static final String SERVICE_CREDENTIAL_BINDING_BY_GUID = "/v3/service_credential_bindings/{guid}"; + public static final String SERVICE_PLAN_VISIBILITY = "/v3/service_plans/{guid}/visibility"; + public static final String PROCESS_BY_GUID = "/v3/processes/{guid}"; + public static final String TASK_CANCEL = "/v3/tasks/{guid}/actions/cancel"; + public static final String PACKAGE_UPLOAD = "/v3/packages/{guid}/upload"; + public static final String SPACE_UNMAPPED_ROUTES = "/v3/spaces/{guid}/routes?unmapped=true"; + + public static final String QUERY_PER_PAGE = "?per_page="; + public static final String QUERY_NAMES = "?names="; + public static final String QUERY_PACKAGE_GUIDS = "?package_guids="; + public static final String QUERY_APP_GUIDS = "?app_guids="; + public static final String QUERY_GUIDS = "?guids="; + public static final String QUERY_SERVICE_INSTANCE_GUIDS = "?service_instance_guids="; + public static final String QUERY_SERVICE_OFFERING_GUIDS = "?service_offering_guids="; + public static final String QUERY_SERVICE_BROKER_GUIDS = "?service_broker_guids="; + public static final String QUERY_ORGANIZATION_GUIDS = "?organization_guids="; + + public static final String AMPERSAND_PER_PAGE = "&per_page="; + public static final String AMPERSAND_SPACE_GUIDS = "&space_guids="; + public static final String AMPERSAND_NAMES = "&names="; + public static final String AMPERSAND_TYPE = "&type="; + public static final String AMPERSAND_LABEL_SELECTOR = "&label_selector="; + public static final String AMPERSAND_DOMAIN_GUIDS = "&domain_guids="; + public static final String AMPERSAND_SERVICE_INSTANCE_GUIDS = "&service_instance_guids="; + public static final String AMPERSAND_SERVICE_OFFERING_GUIDS = "&service_offering_guids="; + public static final String AMPERSAND_SERVICE_BROKER_NAMES = "&service_broker_names="; + public static final String AMPERSAND_APP_GUIDS = "&app_guids="; + public static final String AMPERSAND_USER_GUIDS = "&user_guids="; + public static final String AMPERSAND_TARGET_GUIDS = "&target_guids="; + public static final String AMPERSAND_HOSTS = "&hosts="; + public static final String AMPERSAND_PATHS = "&paths="; + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudSpaceClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudSpaceClient.java index 0eb52d8e0d..0644d62545 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudSpaceClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudSpaceClient.java @@ -1,127 +1,89 @@ package org.cloudfoundry.multiapps.controller.client.facade.rest; +import java.text.MessageFormat; +import java.util.List; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.Messages; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.RawCloudEntity; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudOrganization; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Organization; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ResourceMappers; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Space; import org.cloudfoundry.multiapps.controller.client.facade.util.UriUtil; - -import org.cloudfoundry.AbstractCloudFoundryException; -import org.cloudfoundry.client.v3.ClientV3Exception; -import org.cloudfoundry.client.v3.organizations.GetOrganizationRequest; -import org.cloudfoundry.client.v3.organizations.ListOrganizationsRequest; -import org.cloudfoundry.client.v3.organizations.Organization; -import org.cloudfoundry.client.v3.organizations.OrganizationsV3; -import org.cloudfoundry.client.v3.spaces.GetSpaceRequest; -import org.cloudfoundry.client.v3.spaces.ListSpacesRequest; -import org.cloudfoundry.client.v3.spaces.Space; -import org.cloudfoundry.client.v3.spaces.SpacesV3; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpStatus; -import reactor.util.retry.Retry; -import reactor.util.retry.RetryBackoffSpec; - -import java.time.Duration; -import java.util.List; -import java.util.UUID; public class CloudSpaceClient { private static final List CHARS_TO_ENCODE = List.of(","); - private static final long RETRIES = 3; - private static final Duration RETRY_INTERVAL = Duration.ofSeconds(3); - private final SpacesV3 spacesClient; - private final OrganizationsV3 orgsClient; + private final CloudControllerV3Client cc; - public CloudSpaceClient(SpacesV3 spacesClient, OrganizationsV3 orgsClient) { - this.spacesClient = spacesClient; - this.orgsClient = orgsClient; + public CloudSpaceClient(CloudControllerV3Client cc) { + this.cc = cc; } public CloudSpace getSpace(UUID spaceGuid) { - Space space = spacesClient.get(GetSpaceRequest.builder() - .spaceId(spaceGuid.toString()) - .build()) - .retryWhen(Retry.fixedDelay(RETRIES, RETRY_INTERVAL) - .onRetryExhaustedThrow(this::throwOriginalError)) - .onErrorMap(ClientV3Exception.class, this::convertV3ClientException) - .block(); - if (space == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Space with GUID " + spaceGuid + " not found."); - } + V3Space space = cc.getOptional(CloudControllerV3Endpoints.SPACES + "/" + spaceGuid, V3Space.class) + .orElseThrow(() -> new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SPACE_WITH_GUID_0_NOT_FOUND, + spaceGuid))); + + String orgGuid = space.organizationGuid(); + V3Organization org = cc.getOptional(CloudControllerV3Endpoints.ORGANIZATIONS + "/" + orgGuid, V3Organization.class) + .orElseThrow(() -> new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format( + Messages.ORGANIZATION_WITH_GUID_0_NOT_FOUND, orgGuid))); - String orgGuid = space.getRelationships() - .getOrganization() - .getData() - .getId(); - Organization org = orgsClient.get(GetOrganizationRequest.builder() - .organizationId(orgGuid) - .build()) - .retryWhen(Retry.fixedDelay(RETRIES, RETRY_INTERVAL) - .onRetryExhaustedThrow(this::throwOriginalError)) - .onErrorMap(ClientV3Exception.class, this::convertV3ClientException) - .block(); - if (org == null) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Organization with GUID " + orgGuid + " not found."); - } return mapToCloudSpace(space, org); } public CloudSpace getSpace(String organizationName, String spaceName) { - var orgsResponse = orgsClient.list(ListOrganizationsRequest.builder() - .name(encodeAsQueryParam(organizationName)) - .build()) - .retryWhen(Retry.fixedDelay(RETRIES, RETRY_INTERVAL) - .onRetryExhaustedThrow(this::throwOriginalError)) - .onErrorMap(ClientV3Exception.class, this::convertV3ClientException) - .block(); - List orgs = orgsResponse.getResources(); + List orgs = cc.list(CloudControllerV3Endpoints.ORGANIZATIONS + CloudControllerV3Endpoints.QUERY_NAMES + + encodeAsQueryParam(organizationName), + new ParameterizedTypeReference>() { + }); + if (orgs.isEmpty()) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Organization " + organizationName + " not found."); + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.ORGANISATION_0_NOT_FOUND, organizationName)); } - Organization org = orgs.get(0); - - var spacesResponse = spacesClient.list(ListSpacesRequest.builder() - .organizationId(org.getId()) - .name(encodeAsQueryParam(spaceName)) - .build()) - .retryWhen(Retry.fixedDelay(RETRIES, RETRY_INTERVAL) - .onRetryExhaustedThrow(this::throwOriginalError)) - .onErrorMap(ClientV3Exception.class, this::convertV3ClientException) - .block(); - List spaces = spacesResponse.getResources(); + V3Organization org = orgs.get(0); + + List spaces = cc.list(CloudControllerV3Endpoints.SPACES + CloudControllerV3Endpoints.QUERY_ORGANIZATION_GUIDS + org.guid() + + CloudControllerV3Endpoints.AMPERSAND_NAMES + encodeAsQueryParam(spaceName), + new ParameterizedTypeReference>() { + }); + if (spaces.isEmpty()) { - throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Space " + spaceName + " not found in organization " + organizationName); + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SPACE_0_NOT_FOUND_IN_ORGANIZATION_1, spaceName, + organizationName)); } - Space space = spaces.get(0); - return mapToCloudSpace(space, org); + return mapToCloudSpace(spaces.get(0), org); } private String encodeAsQueryParam(String param) { return UriUtil.encodeChars(param, CHARS_TO_ENCODE); } - private CloudSpace mapToCloudSpace(Space space, Organization org) { + private CloudSpace mapToCloudSpace(V3Space space, V3Organization org) { return ImmutableCloudSpace.builder() - .metadata(RawCloudEntity.parseResourceMetadata(space)) - .name(space.getName()) + .metadata(V3ResourceMappers.parseMetadata(space.guid(), space.createdAt(), space.updatedAt())) + .name(space.name()) .organization(ImmutableCloudOrganization.builder() - .metadata(RawCloudEntity.parseResourceMetadata(org)) - .name(org.getName()) + .metadata(V3ResourceMappers.parseMetadata(org.guid(), + org.createdAt(), + org.updatedAt())) + .name(org.name()) .build()) .build(); } - private Throwable throwOriginalError(RetryBackoffSpec retrySpec, Retry.RetrySignal signal) { - return signal.failure(); - } - - private CloudOperationException convertV3ClientException(AbstractCloudFoundryException e) { - HttpStatus httpStatus = HttpStatus.valueOf(e.getStatusCode()); - return new CloudOperationException(httpStatus, httpStatus.getReasonPhrase(), e.getMessage(), e); - } - } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/DomainsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/DomainsV3Operations.java new file mode 100644 index 0000000000..f219954307 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/DomainsV3Operations.java @@ -0,0 +1,152 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEntity; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Domain; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3DomainMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; + +public class DomainsV3Operations { + + private static final ParameterizedTypeReference> DOMAIN_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public DomainsV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public void addDomain(String domainName) { + assertSpaceProvided("add domain"); + CloudDomain domain = findDomainByName(domainName); + + if (domain == null) { + doCreateDomain(domainName); + } + + } + + public void deleteDomain(String domainName) { + assertSpaceProvided("delete domain"); + CloudDomain domain = findDomainByName(domainName, true); + doDeleteDomain(domain.getGuid()); + } + + public CloudDomain getDefaultDomain() { + V3Domain domain = cc.get(CloudControllerV3Endpoints.ORGANIZATIONS + "/" + getTargetOrganizationGuid() + "/domains/default", + V3Domain.class); + return V3DomainMapper.toCloudDomain(domain); + } + + public List getDomains() { + return getAllDomains().stream() + .map(V3DomainMapper::toCloudDomain) + .collect(Collectors.toList()); + } + + public List getSharedDomains() { + return getAllDomains().stream() + .filter(domain -> !domain.isPrivate()) + .map(V3DomainMapper::toCloudDomain) + .collect(Collectors.toList()); + } + + public List getPrivateDomains() { + return getAllDomains().stream() + .filter(V3Domain::isPrivate) + .map(V3DomainMapper::toCloudDomain) + .collect(Collectors.toList()); + } + + public List getDomainsForOrganization() { + assertSpaceProvided("access organization domains"); + String uri = CloudControllerV3Endpoints.ORGANIZATIONS + "/" + getTargetOrganizationGuid() + "/domains" + + CloudControllerV3Endpoints.QUERY_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, DOMAIN_LIST_TYPE) + .stream() + .map(V3DomainMapper::toCloudDomain) + .collect(Collectors.toList()); + } + + private List getAllDomains() { + return cc.list(CloudControllerV3Endpoints.DOMAINS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, DOMAIN_LIST_TYPE); + } + + private CloudDomain findDomainByName(String name, boolean required) { + CloudDomain domain = findDomainByName(name); + + if (domain == null && required) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.DOMAIN_0_NOT_FOUND, name)); + } + + return domain; + } + + private CloudDomain findDomainByName(String name) { + String uri = CloudControllerV3Endpoints.DOMAINS + CloudControllerV3Endpoints.QUERY_NAMES + name + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, DOMAIN_LIST_TYPE) + .stream() + .findFirst() + .map(V3DomainMapper::toCloudDomain) + .orElse(null); + } + + private void doCreateDomain(String name) { + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.DOMAINS) + .body(Map.of("name", name, "relationships", + Map.of("organization", Map.of("data", Map.of("guid", getTargetOrganizationGuid().toString()))))) + .retrieve() + .toBodilessEntity(); + } + + private void doDeleteDomain(UUID guid) { + ResponseEntity response = cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.DOMAIN_BY_GUID, guid.toString()) + .retrieve() + .toEntity(Void.class); + cc.followAsyncJob(response, Constants.DELETE_JOB_TIMEOUT); + } + + private UUID getTargetOrganizationGuid() { + return getGuid(target.getOrganization()); + } + + private UUID getGuid(CloudEntity entity) { + if (entity == null || entity.getMetadata() == null) { + return null; + } + + return entity.getMetadata() + .getGuid(); + } + + private void assertSpaceProvided(String operation) { + Assert.notNull(target, "Unable to " + operation + " without specifying organization and space to use."); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/EventsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/EventsV3Operations.java new file mode 100644 index 0000000000..e348588f23 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/EventsV3Operations.java @@ -0,0 +1,79 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.List; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Application; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3AuditEvent; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3AuditEventMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; + +public class EventsV3Operations { + + private static final ParameterizedTypeReference> EVENT_PAGE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> APPLICATION_PAGE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public EventsV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public List getEvents() { + return cc.list(CloudControllerV3Endpoints.AUDIT_EVENTS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, EVENT_PAGE) + .stream() + .map(V3AuditEventMapper::toCloudEvent) + .toList(); + } + + public List getEventsByTarget(UUID uuid) { + String query = CloudControllerV3Endpoints.AUDIT_EVENTS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_TARGET_GUIDS + uuid; + + return cc.list(query, EVENT_PAGE) + .stream() + .map(V3AuditEventMapper::toCloudEvent) + .toList(); + } + + public List getApplicationEvents(String applicationName) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + + return getEventsByTarget(applicationGuid); + } + + private UUID getRequiredApplicationGuid(String applicationName) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(applicationName); + List apps = cc.list(query.toString(), APPLICATION_PAGE); + + if (apps.isEmpty()) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + + return UUID.fromString(apps.getFirst() + .guid()); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/HttpConnectionPair.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/HttpConnectionPair.java new file mode 100644 index 0000000000..191f0e73ca --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/HttpConnectionPair.java @@ -0,0 +1,8 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.net.URL; + +import reactor.netty.http.client.HttpClient; + +public record HttpConnectionPair(HttpClient httpClient, URL v3ApiUrl, URL logCacheUrl) { +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/JobsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/JobsV3Operations.java new file mode 100644 index 0000000000..0bb253ebe6 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/JobsV3Operations.java @@ -0,0 +1,21 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Job; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3JobMapper; + +public class JobsV3Operations { + + private final CloudControllerV3Client cc; + + public JobsV3Operations(CloudControllerV3Client cc) { + this.cc = cc; + } + + public CloudAsyncJob getAsyncJob(String jobId) { + V3Job job = cc.get(CloudControllerV3Endpoints.JOBS + "/" + jobId, V3Job.class); + + return V3JobMapper.toCloudAsyncJob(job); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/PackagesV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/PackagesV3Operations.java new file mode 100644 index 0000000000..d58fd4ba14 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/PackagesV3Operations.java @@ -0,0 +1,234 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.nio.file.Path; +import java.text.MessageFormat; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.UploadStatusCallback; +import org.cloudfoundry.multiapps.controller.client.facade.domain.BitsData; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudPackage; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerCredentials; +import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ErrorDetails; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableErrorDetails; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableUpload; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Status; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Upload; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Package; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3PackageMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; + +public class PackagesV3Operations { + + private static final ParameterizedTypeReference> PACKAGE_PAGE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + private final Function uploadRestClientFactory; + + public PackagesV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this(cc, target, null); + } + + public PackagesV3Operations(CloudControllerV3Client cc, CloudSpace target, + Function uploadRestClientFactory) { + this.cc = cc; + this.target = target; + this.uploadRestClientFactory = uploadRestClientFactory; + } + + public CloudPackage getPackage(UUID packageGuid) { + V3Package resource = cc.get(CloudControllerV3Endpoints.PACKAGES + "/" + packageGuid, V3Package.class); + + return resource == null ? null : V3PackageMapper.toCloudPackage(resource); + } + + public List getPackagesForApplication(UUID applicationGuid) { + return cc.list(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/packages" + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, PACKAGE_PAGE) + .stream() + .map(V3PackageMapper::toCloudPackage) + .toList(); + } + + public CloudPackage createDockerPackage(UUID applicationGuid, DockerInfo dockerInfo) { + Map data = new HashMap<>(); + data.put("image", dockerInfo.getImage()); + + DockerCredentials credentials = dockerInfo.getCredentials(); + if (credentials != null) { + if (credentials.getUsername() != null) { + data.put("username", credentials.getUsername()); + } + + if (credentials.getPassword() != null) { + data.put("password", credentials.getPassword()); + } + } + + V3Package created = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.PACKAGES) + .body(Map.of("type", "docker", "data", data, "relationships", + applicationRelationship(applicationGuid))) + .retrieve() + .body(V3Package.class); + + return getPackage(UUID.fromString(created.guid())); + } + + public Upload getUploadStatus(UUID packageGuid) { + CloudPackage cloudPackage = getPackage(packageGuid); + ErrorDetails errorDetails = null; + + if (cloudPackage.getType() == CloudPackage.Type.BITS) { + errorDetails = ImmutableErrorDetails.builder() + .description(((BitsData) cloudPackage.getData()).getError()) + .build(); + } + + return ImmutableUpload.builder() + .status(cloudPackage.getStatus()) + .errorDetails(errorDetails) + .build(); + } + + public CloudPackage asyncUploadApplication(String applicationName, Path file, UploadStatusCallback callback, Duration uploadTimeout) { + CloudPackage cloudPackage = startUpload(applicationName, file, uploadTimeout); + processAsyncUploadInBackground(cloudPackage, callback); + return cloudPackage; + } + + private CloudPackage startUpload(String applicationName, Path file, Duration uploadTimeout) { + Assert.notNull(applicationName, "AppName must not be null"); + Assert.notNull(file, "File must not be null"); + + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + UUID packageGuid = createBitsPackage(applicationGuid).getGuid(); + + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("bits", new FileSystemResource(file)); + + RestClient uploadClient = resolveUploadClient(uploadTimeout); + uploadClient.post() + .uri(CloudControllerV3Endpoints.PACKAGE_UPLOAD, packageGuid) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(body) + .retrieve() + .toBodilessEntity(); + + return getPackage(packageGuid); + } + + private RestClient resolveUploadClient(Duration uploadTimeout) { + if (uploadRestClientFactory != null && uploadTimeout != null && !uploadTimeout.isZero() && !uploadTimeout.isNegative()) { + return uploadRestClientFactory.apply(uploadTimeout); + } + + return cc.getRestClient(); + } + + private CloudPackage createBitsPackage(UUID applicationGuid) { + V3Package created = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.PACKAGES) + .body(Map.of("type", "bits", "relationships", applicationRelationship(applicationGuid))) + .retrieve() + .body(V3Package.class); + + return getPackage(UUID.fromString(created.guid())); + } + + private static Map applicationRelationship(UUID applicationGuid) { + return Map.of("app", java.util.Map.of("data", java.util.Map.of("guid", applicationGuid.toString()))); + } + + private UUID getRequiredApplicationGuid(String applicationName) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(applicationName); + List apps = cc.list(query.toString(), new ParameterizedTypeReference>() { + }); + + if (apps.isEmpty() || apps.getFirst() + .guid() == null) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + + return UUID.fromString(apps.getFirst() + .guid()); + } + + private void processAsyncUploadInBackground(CloudPackage cloudPackage, UploadStatusCallback callback) { + String threadName = String.format("App upload monitor: %s", cloudPackage.getGuid()); + new Thread(() -> processAsyncUpload(cloudPackage, callback), threadName).start(); + } + + private void processAsyncUpload(CloudPackage cloudPackage, UploadStatusCallback callback) { + while (true) { + Upload upload = getUploadStatus(cloudPackage.getGuid()); + Status uploadStatus = upload.getStatus(); + boolean unsubscribe = callback.onProgress(uploadStatus.toString()); + + if (unsubscribe || isUploadReady(uploadStatus)) { + return; + } + + if (hasUploadFailed(uploadStatus)) { + callback.onError(upload.getErrorDetails() + .getDescription()); + return; + } + + try { + Thread.sleep(Constants.PACKAGE_UPLOAD_JOB_POLLING_PERIOD); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + return; + } + } + } + + private boolean isUploadReady(Status status) { + return status == Status.READY; + } + + private boolean hasUploadFailed(Status status) { + return status == Status.EXPIRED || status == Status.FAILED; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record V3App(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ProcessesV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ProcessesV3Operations.java new file mode 100644 index 0000000000..ed19e19222 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ProcessesV3Operations.java @@ -0,0 +1,254 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDropletInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableInstancesInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Application; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Droplet; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3InstancesInfoMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Process; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ProcessMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; + +public class ProcessesV3Operations { + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ProcessesV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public CloudProcess getApplicationProcess(UUID applicationGuid) { + V3Process applicationProcess = cc.get( + CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/processes/" + Constants.WEB_PROCESS_TYPE, + V3Process.class); + + return applicationProcess == null ? null : V3ProcessMapper.toCloudProcess(applicationProcess); + } + + public InstancesInfo getApplicationInstances(CloudApplication application) { + if (application.getState() + .equals(CloudApplication.State.STARTED)) { + return findApplicationInstances(application.getGuid()); + } + + return ImmutableInstancesInfo.builder() + .instances(Collections.emptyList()) + .build(); + } + + public InstancesInfo getApplicationInstances(UUID applicationGuid) { + return findApplicationInstances(applicationGuid); + } + + private InstancesInfo findApplicationInstances(UUID applicationGuid) { + V3Process.V3ProcessStats stats = cc.get(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/processes/" + + Constants.WEB_PROCESS_TYPE + "/stats", + V3Process.V3ProcessStats.class); + + return V3InstancesInfoMapper.toInstancesInfo(stats); + } + + public boolean getApplicationSshEnabled(UUID applicationGuid) { + V3Process.V3SshEnabled sshEnabled = cc.get(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/ssh_enabled", + V3Process.V3SshEnabled.class); + + if (sshEnabled == null || sshEnabled.enabled() == null) { + return false; + } + + return sshEnabled.enabled(); + } + + public Map getApplicationFeatures(UUID applicationGuid) { + List applicationFeatures = cc.list(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/features" + + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, + new ParameterizedTypeReference>() { + }); + + Map result = new HashMap<>(); + for (V3Process.V3AppFeature feature : applicationFeatures) { + result.put(feature.name(), feature.enabled()); + } + + return result; + } + + public DropletInfo getCurrentDropletForApplication(UUID applicationGuid) { + V3Droplet currentDroplet = cc.get(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/droplets/current", V3Droplet.class); + + if (currentDroplet == null) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_WITH_GUID_0_DOES_NOT_HAVE_A_DROPLET, + applicationGuid)); + } + + return parseDropletInfo(currentDroplet); + } + + private DropletInfo parseDropletInfo(V3Droplet droplet) { + String packageUrl = droplet.links() + .get(Constants.PACKAGE_LINK) + .href(); + + if (packageUrl.endsWith("/")) { + packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf("/")); + } + + String packageGuid = packageUrl.substring(packageUrl.lastIndexOf("/") + 1); + return ImmutableDropletInfo.builder() + .guid(UUID.fromString(droplet.guid())) + .packageGuid(UUID.fromString(packageGuid)) + .build(); + } + + public void updateApplicationStaging(String applicationName, Staging staging) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.APP_BY_GUID, applicationGuid) + .body(Map.of("lifecycle", buildApplicationLifecycle(staging))) + .retrieve() + .toBodilessEntity(); + updateApplicationProcess(applicationGuid, staging); + } + + private Map buildApplicationLifecycle(Staging staging) { + if (staging.getDockerInfo() != null) { + return Map.of("type", "docker", "data", Map.of()); + } + + String lifecycleType = staging.getLifecycleType() != null ? staging.getLifecycleType() + .name() + .toLowerCase() + : "buildpack"; + + if ("cnb".equals(lifecycleType) && (staging.getBuildpacks() == null || staging.getBuildpacks() + .isEmpty())) { + throw new IllegalArgumentException("Buildpacks are required for the CNB lifecycle type"); + } + + Map data = new HashMap<>(); + if (staging.getStackName() != null) { + data.put("stack", staging.getStackName()); + } + + if (staging.getBuildpacks() != null) { + data.put("buildpacks", staging.getBuildpacks()); + } + + return Map.of("type", lifecycleType, "data", data); + } + + private void updateApplicationProcess(UUID applicationGuid, Staging staging) { + staging.getAppFeatures() + .forEach((featureName, enabled) -> updateAppFeature(applicationGuid, featureName, enabled)); + + V3Process process = cc.get(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/processes/" + Constants.WEB_PROCESS_TYPE, + V3Process.class); + + Map updateProcessBody = new HashMap<>(); + updateProcessBody.put("command", staging.getCommand()); + if (staging.getHealthCheckType() != null) { + updateProcessBody.put("health_check", buildHealthCheck(staging)); + } + + if (staging.getReadinessHealthCheckType() != null) { + updateProcessBody.put("readiness_health_check", buildReadinessHealthCheck(staging)); + } + + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.PROCESS_BY_GUID, process.guid()) + .body(updateProcessBody) + .retrieve() + .toBodilessEntity(); + } + + private void updateAppFeature(UUID applicationGuid, String featureName, boolean enabled) { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.APP_FEATURE, applicationGuid, featureName) + .body(Map.of("enabled", enabled)) + .retrieve() + .toBodilessEntity(); + } + + private Map buildHealthCheck(Staging staging) { + Map data = new HashMap<>(); + putIfNotNull(data, "endpoint", staging.getHealthCheckHttpEndpoint()); + putIfNotNull(data, "timeout", staging.getHealthCheckTimeout()); + putIfNotNull(data, "invocation_timeout", staging.getInvocationTimeout()); + putIfNotNull(data, "interval", staging.getHealthCheckInterval()); + + Map healthCheck = new HashMap<>(); + healthCheck.put("type", staging.getHealthCheckType()); + healthCheck.put("data", data); + return healthCheck; + } + + private Map buildReadinessHealthCheck(Staging staging) { + Map data = new HashMap<>(); + putIfNotNull(data, "invocation_timeout", staging.getReadinessHealthCheckInvocationTimeout()); + putIfNotNull(data, "endpoint", staging.getReadinessHealthCheckHttpEndpoint()); + putIfNotNull(data, "interval", staging.getReadinessHealthCheckInterval()); + + Map readinessHealthCheck = new HashMap<>(); + readinessHealthCheck.put("type", staging.getReadinessHealthCheckType()); + readinessHealthCheck.put("data", data); + return readinessHealthCheck; + } + + private static void putIfNotNull(Map map, String key, Object value) { + if (value != null) { + map.put(key, value); + } + } + + private UUID getRequiredApplicationGuid(String applicationName) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(applicationName); + + List apps = cc.list(query.toString(), + new ParameterizedTypeReference>() { + }); + + if (apps.isEmpty()) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + + return UUID.fromString(apps.getFirst() + .guid()); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ReactiveFanOut.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ReactiveFanOut.java new file mode 100644 index 0000000000..5743f7be7d --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ReactiveFanOut.java @@ -0,0 +1,33 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.util.List; +import java.util.function.Function; + +import org.cloudfoundry.multiapps.controller.Constants; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +final class ReactiveFanOut { + + private ReactiveFanOut() { + } + + static List mapConcurrently(List items, Function mapper) { + if (items.isEmpty()) { + return List.of(); + } + + if (items.size() == 1) { + return List.of(mapper.apply(items.getFirst())); + } + + return Flux.fromIterable(items) + .flatMapSequential(item -> Mono.fromCallable(() -> mapper.apply(item)) + .subscribeOn(Schedulers.boundedElastic()), + Constants.DEFAULT_CONCURRENT_TASKS) + .collectList() + .block(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/RolesV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/RolesV3Operations.java new file mode 100644 index 0000000000..4bf20566af --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/RolesV3Operations.java @@ -0,0 +1,36 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.util.EnumSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.UserRole; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Role; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3RoleMapper; +import org.springframework.core.ParameterizedTypeReference; + +public class RolesV3Operations { + + private static final ParameterizedTypeReference> ROLE_PAGE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + + public RolesV3Operations(CloudControllerV3Client cc) { + this.cc = cc; + } + + public Set getUserRolesBySpaceAndUser(UUID spaceGuid, UUID userGuid) { + String uri = CloudControllerV3Endpoints.ROLES + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS + spaceGuid + + CloudControllerV3Endpoints.AMPERSAND_USER_GUIDS + userGuid; + + return cc.list(uri, ROLE_PAGE) + .stream() + .map(V3RoleMapper::toUserRole) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(UserRole.class))); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/RoutesV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/RoutesV3Operations.java new file mode 100644 index 0000000000..c0363e6d90 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/RoutesV3Operations.java @@ -0,0 +1,381 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudRoute; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.RouteDestination; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Domain; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Route; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3RouteMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +public class RoutesV3Operations { + + private static final ParameterizedTypeReference> ROUTE_PAGE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> DOMAIN_PAGE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public RoutesV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public void addRoute(String host, String domainName, String path) { + assertSpaceProvided("add route for domain"); + UUID domainGuid = getRequiredDomainGuid(domainName); + doAddRoute(domainGuid, host, path); + } + + public void deleteRoute(String host, String domainName, String path) { + assertSpaceProvided("delete route for domain"); + + UUID routeGuid = getRouteGuid(getRequiredDomainGuid(domainName), host, path); + if (routeGuid == null) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.HOST_0_NOT_FOUND_FOR_DOMAIN_1, host, domainName)); + } + + doDeleteRoute(routeGuid); + } + + public void deleteOrphanedRoutes() { + ResponseEntity response = cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.SPACE_UNMAPPED_ROUTES, getTargetSpaceGuid()) + .retrieve() + .toEntity(Void.class); + + cc.followAsyncJob(response, Constants.DELETE_JOB_TIMEOUT); + } + + public List getRoutes(String domainName) { + assertSpaceProvided("get routes for domain"); + UUID domainGuid = getRequiredDomainGuid(domainName); + return findRoutesByDomainGuid(domainGuid); + } + + public List getApplicationRoutes(UUID applicationGuid) { + return listRoutes(CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/routes" + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE).stream() + .map(route -> V3RouteMapper.toCloudRoute( + route, applicationGuid)) + .toList(); + } + + public void updateApplicationRoutes(String applicationName, Set updatedRoutes) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + + List appRoutes = listRoutes( + CloudControllerV3Endpoints.APPS + "/" + applicationGuid + "/routes" + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE).stream() + .map(V3RouteMapper::toCloudRoute) + .toList(); + + Set outdatedRoutes = getOutdatedRoutes(applicationGuid, appRoutes, updatedRoutes); + Set newRoutes = getNewRoutes(applicationGuid, appRoutes, updatedRoutes); + + removeRoutes(outdatedRoutes, applicationGuid); + addRoutes(newRoutes, applicationGuid); + } + + private List findRoutesByDomainGuid(UUID domainGuid) { + StringBuilder query = new StringBuilder( + CloudControllerV3Endpoints.ROUTES + CloudControllerV3Endpoints.QUERY_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + query.append(CloudControllerV3Endpoints.AMPERSAND_DOMAIN_GUIDS) + .append(domainGuid); + + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(getTargetSpaceGuid()); + + return listRoutes(query.toString()).stream() + .map(V3RouteMapper::toCloudRoute) + .toList(); + } + + private UUID getRouteGuid(UUID domainGuid, String host, String path) { + StringBuilder query = new StringBuilder( + CloudControllerV3Endpoints.ROUTES + CloudControllerV3Endpoints.QUERY_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + query.append(CloudControllerV3Endpoints.AMPERSAND_DOMAIN_GUIDS) + .append(domainGuid); + + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(getTargetSpaceGuid()); + + if (host != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_HOSTS) + .append(host); + } + + if (path != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_PATHS) + .append(path); + } + + List routes = listRoutes(query.toString()); + if (CollectionUtils.isEmpty(routes)) { + return null; + } + + return UUID.fromString(routes.getFirst() + .guid()); + } + + private List listRoutes(String query) { + return cc.list(query, ROUTE_PAGE); + } + + private void addRoutes(Set routes, UUID applicationGuid) { + Map domains = getDomainsFromRoutes(routes); + + for (CloudRoute route : routes) { + validateDomainForRoute(route, domains); + + UUID domainGuid = domains.get(route.getDomain() + .getName()); + UUID routeGuid = getOrAddRoute(domainGuid, route.getHost(), route.getPath()); + + bindRoute(routeGuid, applicationGuid, route.getRequestedProtocol()); + } + } + + private void removeRoutes(Set routes, UUID applicationGuid) { + for (CloudRoute route : routes) { + for (RouteDestination destination : route.getDestinations()) { + if (destination.getApplicationGuid() + .equals(applicationGuid)) { + unbindRoute(route.getGuid(), destination.getGuid()); + } + } + } + } + + private UUID getOrAddRoute(UUID domainGuid, String host, String path) { + UUID routeGuid = getRouteGuid(domainGuid, host, path); + + if (routeGuid == null) { + routeGuid = doAddRoute(domainGuid, host, path); + } + + return routeGuid; + } + + private UUID doAddRoute(UUID domainGuid, String host, String path) { + assertSpaceProvided("add route"); + V3Route created = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.ROUTES) + .body(Map.of("host", host == null ? "" : host, "path", path == null ? "" : path, "relationships", + Map.of("domain", toOneRelationship(domainGuid), "space", + toOneRelationship(getTargetSpaceGuid())))) + .retrieve() + .body(V3Route.class); + + return UUID.fromString(created.guid()); + } + + private void doDeleteRoute(UUID guid) { + ResponseEntity response = cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.ROUTE_BY_GUID, guid) + .retrieve() + .toEntity(Void.class); + + cc.followAsyncJob(response, Constants.DELETE_JOB_TIMEOUT); + } + + private void bindRoute(UUID routeGuid, UUID applicationGuid, String protocol) { + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.ROUTE_DESTINATIONS, routeGuid) + .body(Map.of("destinations", List.of(createDestination(applicationGuid, protocol)))) + .retrieve() + .toBodilessEntity(); + } + + private void unbindRoute(UUID routeGuid, UUID destinationGuid) { + cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.ROUTE_DESTINATION_BY_GUID, routeGuid, destinationGuid) + .retrieve() + .toBodilessEntity(); + } + + private Map createDestination(UUID applicationGuid, String protocol) { + if (protocol == null) { + return Map.of("app", Map.of("guid", applicationGuid.toString())); + } + + return Map.of("app", Map.of("guid", applicationGuid.toString()), "protocol", protocol); + } + + private static Map toOneRelationship(UUID guid) { + return Map.of("data", Map.of("guid", guid.toString())); + } + + private Set getOutdatedRoutes(UUID applicationGuid, List currentRoutes, Set updatedRoutes) { + return currentRoutes.stream() + .filter(currentRoute -> isRouteOutdated(applicationGuid, currentRoute, updatedRoutes)) + .collect(Collectors.toSet()); + } + + private boolean isRouteOutdated(UUID applicationGuid, CloudRoute currentRoute, Set updatedRoutes) { + Optional updatedRoute = findRoute(currentRoute.getUrl(), updatedRoutes); + + if (updatedRoute.isEmpty()) { + return true; + } + + return isProtocolChanged(applicationGuid, currentRoute, updatedRoute.get()); + } + + private Set getNewRoutes(UUID applicationGuid, List currentRoutes, Set updatedRoutes) { + return updatedRoutes.stream() + .filter(updatedRoute -> isRouteUpdated(applicationGuid, updatedRoute, currentRoutes)) + .collect(Collectors.toSet()); + } + + private boolean isRouteUpdated(UUID applicationGuid, CloudRoute updatedRoute, List currentRoutes) { + Optional currentRoute = findRoute(updatedRoute.getUrl(), currentRoutes); + + if (currentRoute.isEmpty()) { + return true; + } + + return isProtocolChanged(applicationGuid, currentRoute.get(), updatedRoute); + } + + private Optional findRoute(String url, java.util.Collection routes) { + return routes.stream() + .filter(route -> Objects.equals(url, route.getUrl())) + .findFirst(); + } + + private boolean isProtocolChanged(UUID applicationGuid, CloudRoute currentRoute, CloudRoute updatedRoute) { + if (updatedRoute.getRequestedProtocol() == null) { + return false; + } + + return currentRoute.getDestinations() + .stream() + .filter(routeDestination -> Objects.equals(routeDestination.getApplicationGuid(), applicationGuid)) + .noneMatch(routeDestination -> Objects.equals(routeDestination.getProtocol(), + updatedRoute.getRequestedProtocol())); + } + + private void validateDomainForRoute(CloudRoute route, Map existingDomains) { + String domain = route.getDomain() + .getName(); + + if (!StringUtils.hasLength(domain) || !existingDomains.containsKey(domain)) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.DOMAIN_0_NOT_FOUND_FOR_URI_1, domain, route.getUrl())); + } + + } + + private UUID getRequiredDomainGuid(String name) { + UUID guid = findDomainGuidByName(name); + + if (guid == null) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.DOMAIN_0_NOT_FOUND, name)); + } + + return guid; + } + + private UUID findDomainGuidByName(String name) { + List domains = cc.list(CloudControllerV3Endpoints.DOMAINS + CloudControllerV3Endpoints.QUERY_NAMES + name + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, + DOMAIN_PAGE); + + if (CollectionUtils.isEmpty(domains)) { + return null; + } + + return UUID.fromString(domains.getFirst() + .guid()); + } + + private Map getDomainsFromRoutes(Set routes) { + Set domainNames = routes.stream() + .map(route -> route.getDomain() + .getName()) + .filter(StringUtils::hasLength) + .collect(Collectors.toSet()); + + if (domainNames.isEmpty()) { + return Map.of(); + } + + String names = String.join(",", domainNames); + return cc.list(CloudControllerV3Endpoints.DOMAINS + CloudControllerV3Endpoints.QUERY_NAMES + names + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, DOMAIN_PAGE) + .stream() + .collect(Collectors.toMap(V3Domain::name, domain -> UUID.fromString(domain.guid()))); + } + + private UUID getRequiredApplicationGuid(String applicationName) { + StringBuilder query = new StringBuilder( + CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(applicationName); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + List apps = cc.list(query.toString(), new ParameterizedTypeReference>() { + }); + + if (CollectionUtils.isEmpty(apps)) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + + return UUID.fromString(apps.getFirst() + .guid()); + } + + private UUID getTargetSpaceGuid() { + return target.getGuid(); + } + + private void assertSpaceProvided(String operation) { + if (target == null) { + throw new IllegalArgumentException( + MessageFormat.format(Messages.UNABLE_TO_0_WITHOUT_SPECIFYING_ORGANIZATION_AND_SPACE_TO_USE, operation)); + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record V3AppRef(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceBindingsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceBindingsV3Operations.java new file mode 100644 index 0000000000..5124d1aec4 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceBindingsV3Operations.java @@ -0,0 +1,279 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.net.URI; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Application; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceBindingMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.CollectionUtils; + +public class ServiceBindingsV3Operations { + + private static final ParameterizedTypeReference> BINDING_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> APPLICATION_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ServiceBindingsV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public Optional bindServiceInstance(String bindingName, String applicationName, String serviceInstanceName) { + return bindServiceInstance(bindingName, applicationName, serviceInstanceName, null); + } + + public Optional bindServiceInstance(String bindingName, String applicationName, String serviceInstanceName, + Map parameters) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceInstanceName); + + Map body = new HashMap<>(); + body.put("name", bindingName); + body.put("type", "app"); + body.put("relationships", Map.of("app", toOneRelationship(applicationGuid), "service_instance", + toOneRelationship(serviceInstanceGuid))); + + if (!CollectionUtils.isEmpty(parameters)) { + body.put("parameters", parameters); + } + + ResponseEntity response = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS) + .body(body) + .retrieve() + .toBodilessEntity(); + + return extractJobGuidFromResponseHeader(response); + } + + public List unbindServiceInstance(String applicationName, String serviceInstanceName) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceInstanceName); + + return doUnbindServiceInstance(applicationGuid, serviceInstanceGuid); + } + + public List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { + return doUnbindServiceInstance(applicationGuid, serviceInstanceGuid); + } + + public Optional deleteServiceBinding(UUID bindingGuid) { + return deleteSingleServiceBinding(bindingGuid); + } + + public CloudServiceBinding getServiceBinding(UUID serviceBindingGuid) { + String uri = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_GUIDS + serviceBindingGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, BINDING_LIST_TYPE) + .stream() + .findFirst() + .map(V3ServiceBindingMapper::toCloudServiceBinding) + .orElse(null); + } + + public List getServiceAppBindings(UUID serviceInstanceGuid) { + String uri = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_SERVICE_INSTANCE_GUIDS + + serviceInstanceGuid + CloudControllerV3Endpoints.AMPERSAND_TYPE + "app" + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return listBindings(uri); + } + + public List getAppBindings(UUID applicationGuid) { + String uri = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_APP_GUIDS + applicationGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return listBindings(uri); + } + + public List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid) { + String uri = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_APP_GUIDS + applicationId + + CloudControllerV3Endpoints.AMPERSAND_SERVICE_INSTANCE_GUIDS + serviceInstanceGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return listBindings(uri); + } + + public Map getServiceBindingParameters(UUID guid) { + @SuppressWarnings("unchecked") + Map parameters = cc.get(CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + "/" + guid + "/parameters", + Map.class); + + return parameters; + } + + public void updateServiceBindingMetadata(UUID guid, Metadata metadata) { + Map metadataBody = new HashMap<>(); + metadataBody.put("labels", metadata.getLabels()); + metadataBody.put("annotations", metadata.getAnnotations()); + + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDING_BY_GUID, guid) + .body(Map.of("metadata", metadataBody)) + .retrieve() + .toBodilessEntity(); + } + + private List listBindings(String uri) { + return cc.list(uri, BINDING_LIST_TYPE) + .stream() + .map(V3ServiceBindingMapper::toCloudServiceBinding) + .toList(); + } + + private List doUnbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { + List serviceBindingGuids = getServiceBindingGuids(applicationGuid, serviceInstanceGuid); + + if (serviceBindingGuids.isEmpty()) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, MessageFormat.format( + Messages.SERVICE_BINDING_BETWEEN_SERVICE_WITH_GUID_0_AND_APPLICATION_WITH_GUID_1_NOT_FOUND, serviceInstanceGuid, + applicationGuid)); + } + + return doDeleteServiceBindings(serviceBindingGuids); + } + + private List getServiceBindingGuids(UUID applicationGuid, UUID serviceInstanceGuid) { + String uri = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_APP_GUIDS + applicationGuid + + CloudControllerV3Endpoints.AMPERSAND_SERVICE_INSTANCE_GUIDS + serviceInstanceGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, BINDING_LIST_TYPE) + .stream() + .map(binding -> UUID.fromString(binding.guid())) + .toList(); + } + + private List doDeleteServiceBindings(List guids) { + List jobIds = new ArrayList<>(); + List errors = new ArrayList<>(); + + for (UUID guid : guids) { + try { + deleteSingleServiceBinding(guid).ifPresent(jobIds::add); + } catch (CloudOperationException e) { + errors.add(e); + } + } + + throwOnErrors(errors); + return jobIds; + } + + private Optional deleteSingleServiceBinding(UUID guid) { + ResponseEntity response = cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDING_BY_GUID, guid) + .retrieve() + .toBodilessEntity(); + + return extractJobGuidFromResponseHeader(response); + } + + private void throwOnErrors(List errors) { + if (errors.isEmpty()) { + return; + } + + CloudOperationException firstException = errors.getFirst(); + errors.subList(1, errors.size()) + .forEach(firstException::addSuppressed); + + throw firstException; + } + + private Optional extractJobGuidFromResponseHeader(ResponseEntity response) { + URI location = response.getHeaders() + .getLocation(); + + if (location == null) { + return Optional.empty(); + } + + String path = location.getPath(); + return Optional.of(path.substring(path.lastIndexOf('/') + 1)); + } + + private Map toOneRelationship(UUID guid) { + return Map.of("data", Map.of("guid", guid.toString())); + } + + private UUID getRequiredApplicationGuid(String applicationName) { + StringBuilder query = new StringBuilder( + CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(applicationName); + + List apps = cc.list(query.toString(), APPLICATION_LIST_TYPE); + + if (apps.isEmpty()) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + + return UUID.fromString(apps.getFirst() + .guid()); + } + + private UUID getRequiredServiceInstanceGuid(String name) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.SERVICE_INSTANCES + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(name); + List instances = cc.list(query.toString(), + new ParameterizedTypeReference>() { + }); + + if (instances.isEmpty()) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_INSTANCE_0_NOT_FOUND, name)); + } + + return UUID.fromString(instances.getFirst() + .guid()); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record V3ServiceInstanceRef(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceBrokersV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceBrokersV3Operations.java new file mode 100644 index 0000000000..6b1db34c3e --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceBrokersV3Operations.java @@ -0,0 +1,222 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.net.URI; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBroker; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceBroker; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceBrokerMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; + +public class ServiceBrokersV3Operations { + + private static final ParameterizedTypeReference> BROKER_PAGE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> OFFERING_PAGE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> PLAN_PAGE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ServiceBrokersV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public String createServiceBroker(CloudServiceBroker serviceBroker) { + Assert.notNull(serviceBroker, "Service broker must not be null."); + + Map body = new HashMap<>(); + body.put("name", serviceBroker.getName()); + body.put("url", serviceBroker.getUrl()); + body.put("authentication", basicAuthentication(serviceBroker)); + + if (serviceBroker.getSpaceGuid() != null) { + body.put("relationships", Map.of("space", Map.of("data", Map.of("guid", serviceBroker.getSpaceGuid())))); + } + + ResponseEntity response = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.SERVICE_BROKERS) + .body(body) + .retrieve() + .toBodilessEntity(); + return extractJobGuid(response); + } + + public String deleteServiceBroker(String name) { + CloudServiceBroker broker = getServiceBroker(name); + UUID guid = broker.getMetadata() + .getGuid(); + + ResponseEntity response = cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.SERVICE_BROKER_BY_GUID, guid.toString()) + .retrieve() + .toBodilessEntity(); + + return extractJobGuid(response); + } + + public CloudServiceBroker getServiceBroker(String name) { + return getServiceBroker(name, true); + } + + public CloudServiceBroker getServiceBroker(String name, boolean required) { + CloudServiceBroker serviceBroker = findServiceBrokerByName(name); + + if (serviceBroker == null && required) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_BROKER_0_NOT_FOUND, name)); + } + + return serviceBroker; + } + + public List getServiceBrokers() { + return cc.list(CloudControllerV3Endpoints.SERVICE_BROKERS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, BROKER_PAGE) + .stream() + .map(V3ServiceBrokerMapper::toCloudServiceBroker) + .toList(); + } + + public String updateServiceBroker(CloudServiceBroker serviceBroker) { + Assert.notNull(serviceBroker, "Service broker must not be null."); + + CloudServiceBroker existingBroker = getServiceBroker(serviceBroker.getName()); + UUID brokerGuid = existingBroker.getMetadata() + .getGuid(); + + Map body = Map.of("name", serviceBroker.getName(), "url", serviceBroker.getUrl(), "authentication", + basicAuthentication(serviceBroker)); + + ResponseEntity response = cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.SERVICE_BROKER_BY_GUID, brokerGuid.toString()) + .body(body) + .retrieve() + .toBodilessEntity(); + + return extractJobGuid(response); + } + + public void updateServicePlanVisibilityForBroker(String name, ServicePlanVisibility visibility) { + CloudServiceBroker broker = getServiceBroker(name); + UUID brokerGuid = broker.getMetadata() + .getGuid(); + + for (UUID servicePlanGuid : findServicePlanGuidsByBrokerGuid(brokerGuid)) { + updateServicePlanVisibility(servicePlanGuid, visibility); + } + } + + private CloudServiceBroker findServiceBrokerByName(String name) { + return cc.list( + CloudControllerV3Endpoints.SERVICE_BROKERS + CloudControllerV3Endpoints.QUERY_NAMES + name + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, BROKER_PAGE) + .stream() + .findFirst() + .map(V3ServiceBrokerMapper::toCloudServiceBroker) + .orElse(null); + } + + private List findServicePlanGuidsByBrokerGuid(UUID brokerGuid) { + return findServiceOfferingGuidsByBrokerGuid(brokerGuid).stream() + .flatMap(offeringGuid -> findServicePlanGuidsByOfferingGuid( + offeringGuid).stream()) + .toList(); + } + + private List findServiceOfferingGuidsByBrokerGuid(UUID brokerGuid) { + String uri = CloudControllerV3Endpoints.SERVICE_OFFERINGS + CloudControllerV3Endpoints.QUERY_SERVICE_BROKER_GUIDS + brokerGuid + + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS + getTargetSpaceGuid() + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, OFFERING_PAGE) + .stream() + .map(ServiceOfferingRef::guid) + .map(UUID::fromString) + .toList(); + } + + private List findServicePlanGuidsByOfferingGuid(UUID serviceOfferingGuid) { + String uri = + CloudControllerV3Endpoints.SERVICE_PLANS + CloudControllerV3Endpoints.QUERY_SERVICE_OFFERING_GUIDS + serviceOfferingGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, PLAN_PAGE) + .stream() + .map(ServicePlanRef::guid) + .map(UUID::fromString) + .toList(); + } + + private void updateServicePlanVisibility(UUID servicePlanGuid, ServicePlanVisibility visibility) { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.SERVICE_PLAN_VISIBILITY, servicePlanGuid.toString()) + .body(Map.of("type", visibility.toString())) + .retrieve() + .toBodilessEntity(); + } + + private static Map basicAuthentication(CloudServiceBroker serviceBroker) { + return Map.of("type", "basic", "credentials", + Map.of("username", nullToEmpty(serviceBroker.getUsername()), "password", nullToEmpty(serviceBroker.getPassword()))); + } + + private static String nullToEmpty(String value) { + return value == null ? "" : value; + } + + private String getTargetSpaceGuid() { + return target.getMetadata() + .getGuid() + .toString(); + } + + private static String extractJobGuid(ResponseEntity response) { + URI location = response.getHeaders() + .getLocation(); + + if (location == null) { + return null; + } + + String value = location.toString(); + int index = value.lastIndexOf("/v3/jobs/"); + + if (index < 0) { + return value.substring(value.lastIndexOf('/') + 1); + } + + return value.substring(index + "/v3/jobs/".length()); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record ServiceOfferingRef(@JsonProperty("guid") String guid) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record ServicePlanRef(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceInstancesV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceInstancesV3Operations.java new file mode 100644 index 0000000000..e004dcc6be --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceInstancesV3Operations.java @@ -0,0 +1,501 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEntity; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceInstanceMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceOffering; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServicePlan; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; +import org.springframework.util.Assert; + +public class ServiceInstancesV3Operations { + + private static final ParameterizedTypeReference> SERVICE_INSTANCE_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ServiceInstancesV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public void createServiceInstance(CloudServiceInstance serviceInstance) { + assertSpaceProvided("create service instance"); + Assert.notNull(serviceInstance, "Service instance must not be null."); + UUID servicePlanGuid = findPlanGuidForService(serviceInstance, serviceInstance.getPlan()); + + Map body = new HashMap<>(); + body.put("type", "managed"); + body.put("name", serviceInstance.getName()); + putIfNotNull(body, "metadata", toMetadataMap(serviceInstance.getV3Metadata())); + putIfNotNull(body, "tags", serviceInstance.getTags()); + putIfNotNull(body, "parameters", serviceInstance.getCredentials()); + body.put("relationships", Map.of("service_plan", toOneRelationship(servicePlanGuid.toString()), "space", + toOneRelationship(getTargetSpaceGuid().toString()))); + + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.SERVICE_INSTANCES) + .body(body) + .retrieve() + .toBodilessEntity(); + } + + public void createUserProvidedServiceInstance(CloudServiceInstance serviceInstance) { + assertSpaceProvided("create service instance"); + Assert.notNull(serviceInstance, "Service instance must not be null."); + String syslogDrainUrl = hasText(serviceInstance.getSyslogDrainUrl()) ? serviceInstance.getSyslogDrainUrl() : ""; + + Map body = new HashMap<>(); + body.put("type", "user-provided"); + body.put("name", serviceInstance.getName()); + putIfNotNull(body, "metadata", toMetadataMap(serviceInstance.getV3Metadata())); + putIfNotNull(body, "credentials", serviceInstance.getCredentials()); + body.put("syslog_drain_url", syslogDrainUrl); + putIfNotNull(body, "tags", serviceInstance.getTags()); + body.put("relationships", Map.of("space", toOneRelationship(getTargetSpaceGuid().toString()))); + + cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.SERVICE_INSTANCES) + .body(body) + .retrieve() + .toBodilessEntity(); + } + + public void deleteServiceInstance(String serviceInstanceName) { + CloudServiceInstance serviceInstance = getServiceInstanceWithoutAuxiliaryContent(serviceInstanceName); + doDeleteServiceInstance(serviceInstance.getGuid()); + } + + public void deleteServiceInstance(CloudServiceInstance serviceInstance) { + doDeleteServiceInstance(serviceInstance.getGuid()); + } + + public UUID getRequiredServiceInstanceGuid(String name) { + V3ServiceInstance resource = findServiceInstanceResourceByName(name); + + if (resource == null) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_INSTANCE_0_NOT_FOUND, name)); + } + + return UUID.fromString(resource.guid()); + } + + public CloudServiceInstance getServiceInstance(String serviceInstanceName) { + return getServiceInstance(serviceInstanceName, true); + } + + public CloudServiceInstance getServiceInstance(String serviceInstanceName, boolean required) { + CloudServiceInstance serviceInstance = findServiceInstanceByName(serviceInstanceName); + + return getServiceInstanceIfRequired(serviceInstanceName, serviceInstance, required); + } + + public String getServiceInstanceName(UUID serviceInstanceGuid) { + V3ServiceInstance serviceInstance = cc.get(CloudControllerV3Endpoints.SERVICE_INSTANCES + "/" + serviceInstanceGuid, + V3ServiceInstance.class); + + return serviceInstance == null ? null : serviceInstance.name(); + } + + public CloudServiceInstance getServiceInstanceWithoutAuxiliaryContent(String serviceInstanceName) { + return getServiceInstanceWithoutAuxiliaryContent(serviceInstanceName, true); + } + + public CloudServiceInstance getServiceInstanceWithoutAuxiliaryContent(String serviceInstanceName, boolean required) { + V3ServiceInstance resource = findServiceInstanceResourceByName(serviceInstanceName); + + CloudServiceInstance serviceInstance = resource == null ? null : V3ServiceInstanceMapper.toCloudServiceInstance(resource, null, + null); + + return getServiceInstanceIfRequired(serviceInstanceName, serviceInstance, required); + } + + public Map getServiceInstanceParameters(UUID guid) { + return getParameters(CloudControllerV3Endpoints.SERVICE_INSTANCES + "/" + guid + "/parameters"); + } + + public Map getUserProvidedServiceInstanceParameters(UUID guid) { + return getParameters(CloudControllerV3Endpoints.SERVICE_INSTANCES + "/" + guid + "/credentials"); + } + + public List getServiceInstancesWithoutAuxiliaryContentByNames(List names) { + List allServiceInstances = new ArrayList<>(); + + for (List batch : toBatches(names, Constants.MAX_CHAR_LENGTH_FOR_PARAMS_IN_REQUEST)) { + String uri = CloudControllerV3Endpoints.SERVICE_INSTANCES + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS + getTargetSpaceGuid() + + CloudControllerV3Endpoints.AMPERSAND_NAMES + String.join(",", batch); + + cc.list(uri, SERVICE_INSTANCE_LIST_TYPE) + .stream() + .map(V3ServiceInstanceMapper::toCloudServiceInstanceWithoutAuxiliaryContent) + .forEach(allServiceInstances::add); + } + + return allServiceInstances; + } + + public List getServiceInstancesByMetadataLabelSelector(String labelSelector) { + String uri = CloudControllerV3Endpoints.SERVICE_INSTANCES + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS + getTargetSpaceGuid() + + CloudControllerV3Endpoints.AMPERSAND_LABEL_SELECTOR + labelSelector; + + return ReactiveFanOut.mapConcurrently(cc.list(uri, SERVICE_INSTANCE_LIST_TYPE), this::mapWithSupportingContent); + } + + public List getServiceInstancesWithoutAuxiliaryContentByMetadataLabelSelector(String labelSelector) { + String uri = CloudControllerV3Endpoints.SERVICE_INSTANCES + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS + getTargetSpaceGuid() + + CloudControllerV3Endpoints.AMPERSAND_LABEL_SELECTOR + labelSelector; + + return cc.list(uri, SERVICE_INSTANCE_LIST_TYPE) + .stream() + .map(resource -> V3ServiceInstanceMapper.toCloudServiceInstance(resource, null, null)) + .collect(Collectors.toList()); + } + + public void updateServicePlan(String serviceName, String planName) { + CloudServiceInstance service = getServiceInstance(serviceName); + + if (service.isUserProvided()) { + return; + } + + UUID planGuid = findPlanGuidForService(service, planName); + patchServiceInstance(service.getGuid(), Map.of("relationships", Map.of("service_plan", toOneRelationship(planGuid.toString())))); + } + + public void updateServiceParameters(String serviceName, Map parameters) { + CloudServiceInstance service = getServiceInstanceWithoutAuxiliaryContent(serviceName); + String key = service.isUserProvided() ? "credentials" : "parameters"; + patchServiceInstance(service.getGuid(), Map.of(key, parameters)); + } + + public void updateServiceTags(String serviceName, List tags) { + UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceName); + patchServiceInstance(serviceInstanceGuid, Map.of("tags", tags)); + } + + public void updateServiceSyslogDrainUrl(String serviceName, String syslogDrainUrl) { + CloudServiceInstance service = getServiceInstanceWithoutAuxiliaryContent(serviceName); + + if (!service.isUserProvided()) { + return; + } + + String updatedSyslogDrain = hasText(syslogDrainUrl) ? syslogDrainUrl : ""; + patchServiceInstance(service.getGuid(), Map.of("syslog_drain_url", updatedSyslogDrain)); + } + + public void updateServiceInstanceMetadata(UUID guid, Metadata metadata) { + Map metadataMap = toMetadataMap(metadata); + patchServiceInstance(guid, Map.of("metadata", metadataMap == null ? Map.of() : metadataMap)); + } + + private CloudServiceInstance getServiceInstanceIfRequired(String serviceInstanceName, CloudServiceInstance serviceInstance, + boolean required) { + if (serviceInstance == null && required) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_INSTANCE_0_NOT_FOUND, serviceInstanceName)); + } + + return serviceInstance; + } + + private CloudServiceInstance findServiceInstanceByName(String name) { + V3ServiceInstance resource = findServiceInstanceResourceByName(name); + + if (resource == null) { + return null; + } + + return mapWithSupportingContent(resource); + } + + private CloudServiceInstance mapWithSupportingContent(V3ServiceInstance resource) { + if (isUserProvided(resource)) { + return V3ServiceInstanceMapper.toCloudServiceInstance(resource, null, null); + } + + String servicePlanGuid = servicePlanGuidOf(resource); + if (servicePlanGuid == null) { + return V3ServiceInstanceMapper.toCloudServiceInstance(resource, null, null); + } + + ServicePlanNames names = resolvePlanAndOfferingNames(servicePlanGuid, resource.name()); + return V3ServiceInstanceMapper.toCloudServiceInstance(resource, names.planName(), names.offeringName()); + } + + private V3ServiceInstance findServiceInstanceResourceByName(String name) { + String uri = CloudControllerV3Endpoints.SERVICE_INSTANCES + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS + getTargetSpaceGuid() + + CloudControllerV3Endpoints.AMPERSAND_NAMES + name; + + return cc.list(uri, SERVICE_INSTANCE_LIST_TYPE) + .stream() + .findFirst() + .orElse(null); + } + + private boolean isUserProvided(V3ServiceInstance resource) { + return "user-provided".equals(resource.type()); + } + + private String servicePlanGuidOf(V3ServiceInstance resource) { + if (resource.relationships() == null || resource.relationships() + .servicePlan() == null + || resource.relationships() + .servicePlan() + .data() == null) { + return null; + } + + return resource.relationships() + .servicePlan() + .data() + .guid(); + } + + private ServicePlanNames resolvePlanAndOfferingNames(String servicePlanGuid, String serviceInstanceName) { + V3ServicePlan plan = getServicePlanForNameResolution(servicePlanGuid, serviceInstanceName); + + if (plan == null) { + return new ServicePlanNames(null, null); + } + + String offeringName = null; + String offeringGuid = offeringGuidOf(plan); + + if (offeringGuid != null) { + V3ServiceOffering offering = getServiceOfferingForNameResolution(offeringGuid); + offeringName = offering == null ? null : offering.name(); + } + + return new ServicePlanNames(plan.name(), offeringName); + } + + private V3ServicePlan getServicePlanForNameResolution(String servicePlanGuid, String serviceInstanceName) { + try { + return cc.get(CloudControllerV3Endpoints.SERVICE_PLANS + "/" + servicePlanGuid, V3ServicePlan.class); + } catch (CloudOperationException e) { + if (e.getStatusCode() == HttpStatus.FORBIDDEN) { + throw new CloudOperationException(HttpStatus.FORBIDDEN, Messages.FORBIDDEN, + MessageFormat.format( + Messages.SERVICE_PLAN_WITH_GUID_0_NOT_AVAILABLE_FOR_SERVICE_INSTANCE_1, + servicePlanGuid, serviceInstanceName), + e); + } + + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.NO_SERVICE_PLAN_FOUND, servicePlanGuid, + serviceInstanceName), + e); + } + + throw e; + } + } + + private V3ServiceOffering getServiceOfferingForNameResolution(String offeringGuid) { + try { + return cc.get(CloudControllerV3Endpoints.SERVICE_OFFERINGS + "/" + offeringGuid, V3ServiceOffering.class); + } catch (CloudOperationException e) { + if (e.getStatusCode() == HttpStatus.FORBIDDEN) { + throw new CloudOperationException(HttpStatus.FORBIDDEN, Messages.FORBIDDEN, + MessageFormat.format(Messages.SERVICE_OFFERING_WITH_GUID_0_IS_NOT_AVAILABLE, + offeringGuid), + e); + } + + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_OFFERING_WITH_GUID_0_NOT_FOUND, offeringGuid), e); + } + + throw e; + } + } + + private String offeringGuidOf(V3ServicePlan plan) { + if (plan.relationships() == null || plan.relationships() + .serviceOffering() == null + || plan.relationships() + .serviceOffering() + .data() == null) { + return null; + } + + return plan.relationships() + .serviceOffering() + .data() + .guid(); + } + + private UUID findPlanGuidForService(CloudServiceInstance service, String planName) { + List offeringGuids = findServiceOfferingGuids(service.getLabel(), service.getBroker()); + + for (String offeringGuid : offeringGuids) { + String uri = CloudControllerV3Endpoints.SERVICE_PLANS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_SERVICE_OFFERING_GUIDS + offeringGuid + + CloudControllerV3Endpoints.AMPERSAND_NAMES + planName; + + V3ServicePlan plan = cc.list(uri, new ParameterizedTypeReference>() { + }) + .stream() + .findFirst() + .orElse(null); + + if (plan != null) { + return UUID.fromString(plan.guid()); + } + } + + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_PLAN_0_NOT_FOUND, planName)); + } + + private List findServiceOfferingGuids(String label, String broker) { + StringBuilder uri = new StringBuilder( + CloudControllerV3Endpoints.SERVICE_OFFERINGS + CloudControllerV3Endpoints.QUERY_PER_PAGE).append( + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE) + .append( + CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append( + getTargetSpaceGuid()) + .append( + CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append( + label); + + if (hasText(broker)) { + uri.append(CloudControllerV3Endpoints.AMPERSAND_SERVICE_BROKER_NAMES) + .append(broker); + } + + return cc.list(uri.toString(), new ParameterizedTypeReference>() { + }) + .stream() + .map(V3ServiceOffering::guid) + .collect(Collectors.toList()); + } + + private void doDeleteServiceInstance(UUID serviceInstanceGuid) { + cc.getRestClient() + .delete() + .uri(CloudControllerV3Endpoints.SERVICE_INSTANCE_BY_GUID, serviceInstanceGuid.toString()) + .retrieve() + .toBodilessEntity(); + } + + private void patchServiceInstance(UUID guid, Map body) { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.SERVICE_INSTANCE_BY_GUID, guid.toString()) + .body(body) + .retrieve() + .toBodilessEntity(); + } + + @SuppressWarnings("unchecked") + private Map getParameters(String uri) { + return cc.get(uri, Map.class); + } + + private Map toOneRelationship(String guid) { + return Map.of("data", Map.of("guid", guid)); + } + + private Map toMetadataMap(Metadata metadata) { + if (metadata == null) { + return null; + } + + Map metadataMapResult = new HashMap<>(); + metadataMapResult.put("labels", metadata.getLabels() == null ? Map.of() : metadata.getLabels()); + metadataMapResult.put("annotations", metadata.getAnnotations() == null ? Map.of() : metadata.getAnnotations()); + + return metadataMapResult; + } + + private static void putIfNotNull(Map body, String key, Object value) { + if (value != null) { + body.put(key, value); + } + } + + private static boolean hasText(String value) { + return value != null && !value.isBlank(); + } + + private List> toBatches(Collection largeList, int maxCharLength) { + if (largeList.isEmpty()) { + return List.of(); + } + + List> batches = new ArrayList<>(); + int currentBatchLength = 0; + int currentBatchIndex = 0; + batches.add(new ArrayList<>()); + + for (T element : largeList) { + int elementLength = element.toString() + .length(); + + if (elementLength + currentBatchLength >= maxCharLength) { + batches.add(new ArrayList<>()); + currentBatchIndex++; + currentBatchLength = 0; + } + + batches.get(currentBatchIndex) + .add(element); + currentBatchLength += elementLength; + } + + return batches; + } + + private UUID getTargetSpaceGuid() { + return getGuid(target); + } + + private UUID getGuid(CloudEntity entity) { + if (entity == null || entity.getMetadata() == null) { + return null; + } + + return entity.getMetadata() + .getGuid(); + } + + private void assertSpaceProvided(String operation) { + Assert.notNull(target, "Unable to " + operation + " without specifying organization and space to use."); + } + + private record ServicePlanNames(String planName, String offeringName) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceKeysV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceKeysV3Operations.java new file mode 100644 index 0000000000..b85c8b7aef --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceKeysV3Operations.java @@ -0,0 +1,179 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.Constants; +import org.cloudfoundry.multiapps.controller.client.facade.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceKeyDetails; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceKeyMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; + +public class ServiceKeysV3Operations { + + private final CloudControllerV3Client cc; + + public ServiceKeysV3Operations(CloudControllerV3Client cc) { + this.cc = cc; + } + + public CloudServiceKey createAndFetchServiceKey(CloudServiceKey keyModel, CloudServiceInstance serviceInstance) { + ResponseEntity acceptedResponse = postServiceKey(keyModel.getName(), keyModel.getCredentials(), keyModel.getV3Metadata(), + serviceInstance); + + cc.followAsyncJob(acceptedResponse, Constants.BINDING_OPERATIONS_TIMEOUT); + + V3ServiceBinding createdServiceKey = getServiceKeyResourceByNameAndServiceInstanceGuid(keyModel.getName(), + serviceInstance.getGuid()); + if (createdServiceKey == null) { + return null; + } + + Map credentials = getServiceKeyCredentials(createdServiceKey.guid()); + return V3ServiceKeyMapper.toCloudServiceKey(createdServiceKey, serviceInstance, credentials); + } + + public Optional createServiceKey(CloudServiceKey keyModel, CloudServiceInstance serviceInstance) { + ResponseEntity acceptedResponse = postServiceKey(keyModel.getName(), keyModel.getCredentials(), keyModel.getV3Metadata(), + serviceInstance); + + return extractJobGuid(acceptedResponse); + } + + public Optional createServiceKey(CloudServiceInstance serviceInstance, String serviceKeyName, Map parameters) { + ResponseEntity acceptedResponse = postServiceKey(serviceKeyName, parameters, null, serviceInstance); + + return extractJobGuid(acceptedResponse); + } + + public CloudServiceKey getServiceKey(CloudServiceInstance serviceInstance, String serviceKeyName) { + V3ServiceBinding serviceKey = getServiceKeyResourceByNameAndServiceInstanceGuid(serviceKeyName, serviceInstance.getGuid()); + if (serviceKey == null) { + return null; + } + + Map credentials = getServiceKeyCredentials(serviceKey.guid()); + return V3ServiceKeyMapper.toCloudServiceKey(serviceKey, serviceInstance, credentials); + } + + public List getServiceKeys(CloudServiceInstance serviceInstance) { + return listServiceKeyResources(serviceInstance.getGuid()).stream() + .map(key -> V3ServiceKeyMapper.toCloudServiceKey(key, serviceInstance)) + .toList(); + } + + public List getServiceKeysWithCredentials(CloudServiceInstance serviceInstance) { + return ReactiveFanOut.mapConcurrently(listServiceKeyResources(serviceInstance.getGuid()), + key -> V3ServiceKeyMapper.toCloudServiceKey(key, serviceInstance, + getServiceKeyCredentials(key.guid()))); + } + + private ResponseEntity postServiceKey(String name, Map parameters, Metadata metadata, + CloudServiceInstance serviceInstance) { + if (serviceInstance.getType() != ServiceInstanceType.MANAGED) { + throw new IllegalArgumentException( + String.format(Messages.CANT_CREATE_SERVICE_KEY_FOR_USER_PROVIDED_SERVICE, serviceInstance.getName())); + } + + return cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS) + .body(buildCreateServiceKeyBody(name, parameters, metadata, serviceInstance.getGuid())) + .retrieve() + .toBodilessEntity(); + } + + private Map buildCreateServiceKeyBody(String name, Map parameters, Metadata metadata, + UUID serviceInstanceGuid) { + Map resultBody = new HashMap<>(); + resultBody.put("type", "key"); + resultBody.put("name", name); + resultBody.put("relationships", Map.of("service_instance", Map.of("data", Map.of("guid", serviceInstanceGuid.toString())))); + + if (parameters != null && !parameters.isEmpty()) { + resultBody.put("parameters", parameters); + } + + Map metadataBody = buildMetadataBody(metadata); + if (metadataBody != null) { + resultBody.put("metadata", metadataBody); + } + + return resultBody; + } + + private Map buildMetadataBody(Metadata metadata) { + if (metadata == null) { + return null; + } + + Map metadataBody = new HashMap<>(); + if (metadata.getLabels() != null) { + metadataBody.put("labels", metadata.getLabels()); + } + + if (metadata.getAnnotations() != null) { + metadataBody.put("annotations", metadata.getAnnotations()); + } + + return metadataBody.isEmpty() ? null : metadataBody; + } + + private V3ServiceBinding getServiceKeyResourceByNameAndServiceInstanceGuid(String name, UUID serviceInstanceGuid) { + String query = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_TYPE + "key" + + CloudControllerV3Endpoints.AMPERSAND_SERVICE_INSTANCE_GUIDS + serviceInstanceGuid + CloudControllerV3Endpoints.AMPERSAND_NAMES + + name; + + List keys = cc.list(query, new ParameterizedTypeReference>() { + }); + + return keys.isEmpty() ? null : keys.getFirst(); + } + + private List listServiceKeyResources(UUID serviceInstanceGuid) { + String query = CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_TYPE + "key" + + CloudControllerV3Endpoints.AMPERSAND_SERVICE_INSTANCE_GUIDS + serviceInstanceGuid; + + return cc.list(query, new ParameterizedTypeReference>() { + }); + } + + private Map getServiceKeyCredentials(String keyGuid) { + Optional details = cc.getOptional(CloudControllerV3Endpoints.SERVICE_CREDENTIAL_BINDINGS + "/" + keyGuid + + "/details", + V3ServiceKeyDetails.class); + + return details.map(V3ServiceKeyDetails::credentials) + .orElse(Collections.emptyMap()); + } + + private Optional extractJobGuid(ResponseEntity accepted) { + URI location = accepted.getHeaders() + .getLocation(); + + if (location == null) { + return Optional.empty(); + } + + String href = location.toString(); + int index = href.lastIndexOf("/v3/jobs/"); + String jobGuid = index < 0 ? href.substring(href.lastIndexOf('/') + 1) : href.substring(index + "/v3/jobs/".length()); + + return Optional.of(jobGuid); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceOfferingsV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceOfferingsV3Operations.java new file mode 100644 index 0000000000..8d6cfe3cab --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServiceOfferingsV3Operations.java @@ -0,0 +1,65 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.util.List; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceOffering; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceOffering; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServiceOfferingMapper; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServicePlan; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServicePlanMapper; +import org.springframework.core.ParameterizedTypeReference; + +public class ServiceOfferingsV3Operations { + + private static final ParameterizedTypeReference> OFFERING_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> PLAN_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ServiceOfferingsV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public List getServiceOfferings() { + return getServiceOfferingResources().stream() + .map(this::toCloudServiceOfferingWithPlans) + .toList(); + } + + private List getServiceOfferingResources() { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.SERVICE_OFFERINGS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + return cc.list(query.toString(), OFFERING_LIST_TYPE); + } + + private CloudServiceOffering toCloudServiceOfferingWithPlans(V3ServiceOffering serviceOffering) { + List servicePlans = getServicePlans(serviceOffering.guid()); + + return V3ServiceOfferingMapper.toCloudServiceOffering(serviceOffering, servicePlans); + } + + private List getServicePlans(String serviceOfferingGuid) { + String uri = + CloudControllerV3Endpoints.SERVICE_PLANS + CloudControllerV3Endpoints.QUERY_SERVICE_OFFERING_GUIDS + serviceOfferingGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, PLAN_LIST_TYPE) + .stream() + .map(V3ServicePlanMapper::toCloudServicePlan) + .toList(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServicePlansV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServicePlansV3Operations.java new file mode 100644 index 0000000000..7a9c033585 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ServicePlansV3Operations.java @@ -0,0 +1,110 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ServicePlan; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; + +public class ServicePlansV3Operations { + + private static final ParameterizedTypeReference> BROKER_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> OFFERING_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + private static final ParameterizedTypeReference> PLAN_LIST_TYPE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public ServicePlansV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public void updateServicePlanVisibilityForBroker(String name, ServicePlanVisibility visibility) { + UUID brokerGuid = getRequiredServiceBrokerGuid(name); + List servicePlanGuids = findServicePlanGuidsByBrokerGuid(brokerGuid); + for (UUID servicePlanGuid : servicePlanGuids) { + updateServicePlanVisibility(servicePlanGuid, visibility); + } + } + + private UUID getRequiredServiceBrokerGuid(String name) { + String uri = CloudControllerV3Endpoints.SERVICE_BROKERS + CloudControllerV3Endpoints.QUERY_NAMES + name + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, BROKER_LIST_TYPE) + .stream() + .findFirst() + .map(broker -> UUID.fromString(broker.guid())) + .orElseThrow(() -> new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.SERVICE_BROKER_0_NOT_FOUND, name))); + } + + private List findServicePlanGuidsByBrokerGuid(UUID brokerGuid) { + List offeredServicesGuids = findServiceOfferingGuidsByBrokerGuid(brokerGuid); + if (offeredServicesGuids.isEmpty()) { + return List.of(); + } + + String offeringGuidsFilter = offeredServicesGuids.stream() + .map(UUID::toString) + .reduce((a, b) -> a + "," + b) + .orElse(""); + + String uri = CloudControllerV3Endpoints.SERVICE_PLANS + CloudControllerV3Endpoints.QUERY_SERVICE_OFFERING_GUIDS + + offeringGuidsFilter + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE; + + return cc.list(uri, PLAN_LIST_TYPE) + .stream() + .map(plan -> UUID.fromString(plan.guid())) + .toList(); + } + + private List findServiceOfferingGuidsByBrokerGuid(UUID brokerGuid) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.SERVICE_OFFERINGS + + CloudControllerV3Endpoints.QUERY_SERVICE_BROKER_GUIDS + brokerGuid + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + return cc.list(query.toString(), OFFERING_LIST_TYPE) + .stream() + .map(offering -> UUID.fromString(offering.guid())) + .toList(); + } + + private void updateServicePlanVisibility(UUID servicePlanGuid, ServicePlanVisibility visibility) { + cc.getRestClient() + .patch() + .uri(CloudControllerV3Endpoints.SERVICE_PLAN_VISIBILITY, servicePlanGuid) + .body(Map.of("type", visibility.toString())) + .retrieve() + .toBodilessEntity(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record V3ServiceBrokerRef(@JsonProperty("guid") String guid) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private record V3ServiceOfferingRef(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/StacksV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/StacksV3Operations.java new file mode 100644 index 0000000000..d8cc570f5a --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/StacksV3Operations.java @@ -0,0 +1,58 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.List; + +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudStack; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Stack; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3StackMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; + +public class StacksV3Operations { + + private static final ParameterizedTypeReference> STACK_PAGE = new ParameterizedTypeReference<>() { + }; + + private final CloudControllerV3Client cc; + + public StacksV3Operations(CloudControllerV3Client cc) { + this.cc = cc; + } + + public CloudStack getStack(String name) { + return getStack(name, true); + } + + public CloudStack getStack(String name, boolean required) { + CloudStack resultStack = findStackByName(name); + + if (resultStack == null && required) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.STACK_0_NOT_FOUND, name)); + } + + return resultStack; + } + + public List getStacks() { + return cc.list(CloudControllerV3Endpoints.STACKS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, STACK_PAGE) + .stream() + .map(V3StackMapper::toCloudStack) + .toList(); + } + + private CloudStack findStackByName(String name) { + return cc.list(CloudControllerV3Endpoints.STACKS + CloudControllerV3Endpoints.QUERY_NAMES + name + + CloudControllerV3Endpoints.AMPERSAND_PER_PAGE + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE, STACK_PAGE) + .stream() + .map(V3StackMapper::toCloudStack) + .findFirst() + .orElse(null); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/TasksV3Operations.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/TasksV3Operations.java new file mode 100644 index 0000000000..486480dd58 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/TasksV3Operations.java @@ -0,0 +1,115 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.Messages; +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudTask; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Application; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3ListResponse; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3Task; +import org.cloudfoundry.multiapps.controller.client.facade.rest.resources.V3TaskMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; + +public class TasksV3Operations { + + private final CloudControllerV3Client cc; + private final CloudSpace target; + + public TasksV3Operations(CloudControllerV3Client cc, CloudSpace target) { + this.cc = cc; + this.target = target; + } + + public CloudTask getTask(UUID taskGuid) { + V3Task task = cc.get(CloudControllerV3Endpoints.TASKS + "/" + taskGuid, V3Task.class); + + return task == null ? null : V3TaskMapper.toCloudTask(task); + } + + public List getTasks(String applicationName) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + + String query = CloudControllerV3Endpoints.TASKS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE + CloudControllerV3Endpoints.AMPERSAND_APP_GUIDS + applicationGuid; + + List tasks = cc.list(query, new ParameterizedTypeReference>() { + }); + + return tasks.stream() + .map(V3TaskMapper::toCloudTask) + .toList(); + } + + public CloudTask runTask(String applicationName, CloudTask task) { + UUID applicationGuid = getRequiredApplicationGuid(applicationName); + + V3Task created = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.APP_TASKS, applicationGuid) + .body(buildCreateTaskBody(task)) + .retrieve() + .body(V3Task.class); + + return created == null ? null : V3TaskMapper.toCloudTask(created); + } + + public CloudTask cancelTask(UUID taskGuid) { + V3Task cancelled = cc.getRestClient() + .post() + .uri(CloudControllerV3Endpoints.TASK_CANCEL, taskGuid) + .retrieve() + .body(V3Task.class); + + return cancelled == null ? null : V3TaskMapper.toCloudTask(cancelled); + } + + private Map buildCreateTaskBody(CloudTask task) { + Map body = new HashMap<>(); + body.put("command", task.getCommand()); + body.put("name", task.getName()); + + CloudTask.Limits limits = task.getLimits(); + if (limits != null) { + if (limits.getMemory() != null) { + body.put("memory_in_mb", limits.getMemory()); + } + + if (limits.getDisk() != null) { + body.put("disk_in_mb", limits.getDisk()); + } + } + + return body; + } + + private UUID getRequiredApplicationGuid(String applicationName) { + StringBuilder query = new StringBuilder(CloudControllerV3Endpoints.APPS + CloudControllerV3Endpoints.QUERY_PER_PAGE + + CloudControllerV3Endpoints.DEFAULT_PAGE_SIZE); + + if (target != null && target.getGuid() != null) { + query.append(CloudControllerV3Endpoints.AMPERSAND_SPACE_GUIDS) + .append(target.getGuid()); + } + + query.append(CloudControllerV3Endpoints.AMPERSAND_NAMES) + .append(applicationName); + List apps = cc.list(query.toString(), new ParameterizedTypeReference>() { + }); + + if (apps.isEmpty()) { + throw new CloudOperationException(HttpStatus.NOT_FOUND, Messages.NOT_FOUND, + MessageFormat.format(Messages.APPLICATION_0_NOT_FOUND, applicationName)); + } + + return UUID.fromString(apps.getFirst() + .guid()); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/LogCacheReadResponse.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/LogCacheReadResponse.java new file mode 100644 index 0000000000..010e348dea --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/LogCacheReadResponse.java @@ -0,0 +1,34 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record LogCacheReadResponse(@JsonProperty("envelopes") EnvelopeBatch envelopes) { + + public List batch() { + if (envelopes == null || envelopes.batch() == null) { + return Collections.emptyList(); + } + + return envelopes.batch(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record EnvelopeBatch(@JsonProperty("batch") List batch) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record Envelope(@JsonProperty("source_id") String sourceId, @JsonProperty("timestamp") Long timestamp, + @JsonProperty("tags") Map tags, @JsonProperty("log") Log log) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record Log(@JsonProperty("payload") String payload, @JsonProperty("type") String type) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Application.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Application.java new file mode 100644 index 0000000000..9394d5c894 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Application.java @@ -0,0 +1,49 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 application resource ({@code GET/POST /v3/apps}). + * + *
+ * { "guid": "...", "name": "...", "state": "STARTED|STOPPED", "created_at": "...", "updated_at": "...",
+ *   "lifecycle": { "type": "buildpack|docker|cnb", "data": { "buildpacks": [...], "stack": "..." } },
+ *   "metadata": { "labels": {...}, "annotations": {...} },
+ *   "relationships": { "space": { "data": { "guid": "..." } } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Application(@JsonProperty("guid") String guid, @JsonProperty("name") String name, @JsonProperty("state") String state, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("lifecycle") V3Lifecycle lifecycle, @JsonProperty("metadata") V3Metadata metadata, + @JsonProperty("relationships") V3Relationships relationships) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Lifecycle(@JsonProperty("type") String type, @JsonProperty("data") V3LifecycleData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3LifecycleData(@JsonProperty("buildpacks") List buildpacks, @JsonProperty("stack") String stack) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Relationships(@JsonProperty("space") V3ToOneRelationship space) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3EnvironmentVariables(@JsonProperty("var") Map var) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ApplicationMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ApplicationMapper.java new file mode 100644 index 0000000000..cfab780cd7 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ApplicationMapper.java @@ -0,0 +1,57 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Collections; +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudApplication; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableLifecycle; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle; +import org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType; + +public final class V3ApplicationMapper { + + private static final String BUILDPACKS = "buildpacks"; + private static final String STACK = "stack"; + + private V3ApplicationMapper() { + } + + public static CloudApplication toCloudApplication(V3Application app, CloudSpace space) { + return ImmutableCloudApplication.builder() + .metadata(V3ResourceMappers.parseMetadata(app.guid(), app.createdAt(), app.updatedAt())) + .v3Metadata(V3ResourceMappers.toV3Metadata(app.metadata())) + .name(app.name()) + .state(parseState(app.state())) + .lifecycle(parseLifecycle(app.lifecycle())) + .space(space) + .build(); + } + + private static CloudApplication.State parseState(String state) { + return state == null ? null : CloudApplication.State.valueOf(state); + } + + private static Lifecycle parseLifecycle(V3Application.V3Lifecycle lifecycle) { + if (lifecycle == null || lifecycle.type() == null) { + return null; + } + + return ImmutableLifecycle.builder() + .type(LifecycleType.valueOf(lifecycle.type() + .toUpperCase())) + .data(extractLifecycleData(lifecycle.data())) + .build(); + } + + private static Map extractLifecycleData(V3Application.V3LifecycleData data) { + if (data == null) { + return Collections.emptyMap(); + } + + return Map.of(BUILDPACKS, data.buildpacks() == null ? Collections.emptyList() : data.buildpacks(), + STACK, data.stack() == null ? "" : data.stack()); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3AuditEvent.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3AuditEvent.java new file mode 100644 index 0000000000..95907cb759 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3AuditEvent.java @@ -0,0 +1,26 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 audit event resource ({@code GET /v3/audit_events}). + * + *
+ * { "guid": "...", "created_at": "...", "updated_at": "...", "type": "audit.app.update",
+ *   "actor":  { "guid": "...", "type": "user",        "name": "..." },
+ *   "target": { "guid": "...", "type": "app",         "name": "..." },
+ *   "data": { ... }, "space": {...}, "organization": {...} }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3AuditEvent(@JsonProperty("guid") String guid, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("type") String type, + @JsonProperty("actor") V3Participant actor, @JsonProperty("target") V3Participant target) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Participant(@JsonProperty("guid") String guid, @JsonProperty("type") String type, + @JsonProperty("name") String name) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3AuditEventMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3AuditEventMapper.java new file mode 100644 index 0000000000..e06b1b1518 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3AuditEventMapper.java @@ -0,0 +1,35 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent.Participant; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudEvent; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudEvent.ImmutableParticipant; + +public final class V3AuditEventMapper { + + private V3AuditEventMapper() { + } + + public static CloudEvent toCloudEvent(V3AuditEvent event) { + return ImmutableCloudEvent.builder() + .metadata(V3ResourceMappers.parseMetadata(event.guid(), event.createdAt(), event.updatedAt())) + .target(parseParticipant(event.target())) + .actor(parseParticipant(event.actor())) + .type(event.type()) + .build(); + } + + private static Participant parseParticipant(V3AuditEvent.V3Participant participant) { + if (participant == null) { + return ImmutableParticipant.builder() + .build(); + } + + return ImmutableParticipant.builder() + .guid(V3ResourceMappers.parseNullableGuid(participant.guid())) + .name(participant.name()) + .type(participant.type()) + .build(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Build.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Build.java new file mode 100644 index 0000000000..15fd5ec357 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Build.java @@ -0,0 +1,35 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 build resource ({@code GET/POST /v3/builds}, {@code GET /v3/apps/{guid}/builds}). + * + *
+ * { "guid": "...", "created_at": "...", "updated_at": "...",
+ *   "state": "STAGING|STAGED|FAILED", "error": "...",
+ *   "created_by": { "guid": "...", "name": "..." },
+ *   "package": { "guid": "..." },
+ *   "droplet": { "guid": "..." } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Build(@JsonProperty("guid") String guid, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("state") String state, + @JsonProperty("error") String error, @JsonProperty("created_by") V3CreatedBy createdBy, + @JsonProperty("package") V3PackageReference inputPackage, @JsonProperty("droplet") V3DropletReference droplet) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3CreatedBy(@JsonProperty("guid") String guid, @JsonProperty("name") String name) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3PackageReference(@JsonProperty("guid") String guid) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3DropletReference(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3BuildMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3BuildMapper.java new file mode 100644 index 0000000000..fffddea5e7 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3BuildMapper.java @@ -0,0 +1,59 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; +import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild.ImmutableCreatedBy; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild.ImmutablePackageInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDropletInfo; + +public final class V3BuildMapper { + + private V3BuildMapper() { + } + + public static CloudBuild toCloudBuild(V3Build build) { + return ImmutableCloudBuild.builder() + .metadata(V3ResourceMappers.parseMetadata(build.guid(), build.createdAt(), build.updatedAt())) + .createdBy(parseCreatedBy(build.createdBy())) + .packageInfo(parsePackageInfo(build.inputPackage())) + .dropletInfo(parseDropletInfo(build.droplet())) + .state(parseState(build.state())) + .error(build.error()) + .build(); + } + + private static CloudBuild.CreatedBy parseCreatedBy(V3Build.V3CreatedBy createdBy) { + if (createdBy == null) { + return null; + } + + return ImmutableCreatedBy.builder() + .guid(V3ResourceMappers.parseNullableGuid(createdBy.guid())) + .name(createdBy.name()) + .build(); + } + + private static CloudBuild.PackageInfo parsePackageInfo(V3Build.V3PackageReference inputPackage) { + if (inputPackage == null) { + return null; + } + + return ImmutablePackageInfo.of(V3ResourceMappers.parseNullableGuid(inputPackage.guid())); + } + + private static DropletInfo parseDropletInfo(V3Build.V3DropletReference droplet) { + if (droplet == null || droplet.guid() == null) { + return null; + } + + return ImmutableDropletInfo.builder() + .guid(V3ResourceMappers.parseNullableGuid(droplet.guid())) + .build(); + } + + private static CloudBuild.State parseState(String state) { + return state == null ? null : CloudBuild.State.valueOf(state); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Domain.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Domain.java new file mode 100644 index 0000000000..2234f4c698 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Domain.java @@ -0,0 +1,38 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 domain resource ({@code GET/POST /v3/domains}, {@code GET /v3/organizations/{guid}/domains}, + * {@code GET /v3/organizations/{guid}/domains/default}). + * + *
+ * { "guid": "...", "name": "example.com", "created_at": "...", "updated_at": "...",
+ *   "metadata": { "labels": {...}, "annotations": {...} },
+ *   "relationships": { "organization": { "data": { "guid": "..." } | null } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Domain(@JsonProperty("guid") String guid, @JsonProperty("name") String name, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("metadata") V3Metadata metadata, @JsonProperty("relationships") V3DomainRelationships relationships) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3DomainRelationships(@JsonProperty("organization") V3ToOneRelationship organization) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + + public boolean isPrivate() { + return relationships != null && relationships.organization() != null && relationships.organization() + .data() != null; + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3DomainMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3DomainMapper.java new file mode 100644 index 0000000000..b0f9af0eed --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3DomainMapper.java @@ -0,0 +1,18 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudDomain; + +public final class V3DomainMapper { + + private V3DomainMapper() { + } + + public static CloudDomain toCloudDomain(V3Domain domain) { + return ImmutableCloudDomain.builder() + .metadata(V3ResourceMappers.parseMetadata(domain.guid(), domain.createdAt(), domain.updatedAt())) + .name(domain.name()) + .build(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Droplet.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Droplet.java new file mode 100644 index 0000000000..f69486e983 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Droplet.java @@ -0,0 +1,22 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of the CF v3 current-droplet response ({@code GET /v3/apps/{guid}/droplets/current}): + * + *
+ * { "guid": "...", "links": { "package": { "href": "https://.../v3/packages/{guid}" }, ... } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Droplet(@JsonProperty("guid") String guid, @JsonProperty("links") Map links) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Link(@JsonProperty("href") String href) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawInstancesInfo.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3InstancesInfoMapper.java similarity index 54% rename from multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawInstancesInfo.java rename to multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3InstancesInfoMapper.java index 2c3a83c130..ed1109da12 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawInstancesInfo.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3InstancesInfoMapper.java @@ -1,46 +1,42 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.applications.GetApplicationProcessStatisticsResponse; -import org.cloudfoundry.client.v3.processes.ProcessStatisticsResource; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableInstanceInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableInstancesInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.InstanceInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.InstanceState; import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; -import org.immutables.value.Value; -@Value.Immutable -public abstract class RawInstancesInfo extends RawCloudEntity { +public final class V3InstancesInfoMapper { - @Value.Parameter - public abstract GetApplicationProcessStatisticsResponse getProcessStatisticsResponse(); + private V3InstancesInfoMapper() { + } - @Override - public InstancesInfo derive() { - var processStats = getProcessStatisticsResponse(); + public static InstancesInfo toInstancesInfo(V3Process.V3ProcessStats processStats) { return ImmutableInstancesInfo.builder() - .instances(parseProcessStatistics(processStats.getResources())) + .instances(parseProcessStatistics(processStats.resources())) .build(); } - private static List parseProcessStatistics(List stats) { + private static List parseProcessStatistics(List stats) { if (stats == null) { return Collections.emptyList(); } + return stats.stream() - .map(RawInstancesInfo::parseProcessStatistic) + .map(V3InstancesInfoMapper::parseProcessStatistic) .collect(Collectors.toList()); } - private static InstanceInfo parseProcessStatistic(ProcessStatisticsResource statsResource) { + private static InstanceInfo parseProcessStatistic(V3Process.V3ProcessStatsResource statsResource) { return ImmutableInstanceInfo.builder() - .index(statsResource.getIndex()) - .state(InstanceState.valueOfWithDefault(statsResource.getState())) - .isRoutable(Boolean.parseBoolean(statsResource.getRoutable())) + .index(statsResource.index()) + .state(InstanceState.valueOfWithDefault(statsResource.state())) + .isRoutable(Boolean.parseBoolean(statsResource.routable())) .build(); } + } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Job.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Job.java new file mode 100644 index 0000000000..2354a2134c --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Job.java @@ -0,0 +1,42 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a Cloud Controller v3 job resource ({@code GET /v3/jobs/{guid}}). + * + *
+ * { "guid": "...", "created_at": "...", "updated_at": "...", "state": "PROCESSING|POLLING|COMPLETE|FAILED", "operation": "...",
+ *   "errors": [ { "detail": "...", "title": "...", "code": N } ], "warnings": [ { "detail": "..." } ] }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Job(@JsonProperty("guid") String guid, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("state") String state, + @JsonProperty("operation") String operation, @JsonProperty("errors") List errors, + @JsonProperty("warnings") List warnings) { + + public boolean isComplete() { + return "COMPLETE".equals(state); + } + + public boolean isFailed() { + return "FAILED".equals(state); + } + + public boolean isTerminal() { + return isComplete() || isFailed(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Error(@JsonProperty("detail") String detail, @JsonProperty("title") String title, @JsonProperty("code") Integer code) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Warning(@JsonProperty("detail") String detail) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3JobMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3JobMapper.java new file mode 100644 index 0000000000..b643ed0130 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3JobMapper.java @@ -0,0 +1,55 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.List; +import java.util.stream.Collectors; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; + +public final class V3JobMapper { + + private V3JobMapper() { + } + + public static CloudAsyncJob toCloudAsyncJob(V3Job job) { + return ImmutableCloudAsyncJob.builder() + .metadata(V3ResourceMappers.parseMetadata(job.guid(), job.createdAt(), job.updatedAt())) + .state(parseState(job.state())) + .operation(job.operation()) + .warnings(getWarnings(job)) + .errors(getErrors(job)) + .build(); + } + + private static JobState parseState(String state) { + return state == null ? null : JobState.valueOf(state.toUpperCase()); + } + + private static String getWarnings(V3Job job) { + List warnings = job.warnings(); + if (warnings == null) { + return ""; + } + + return warnings.stream() + .map(V3Job.V3Warning::detail) + .collect(Collectors.joining(",")); + } + + private static String getErrors(V3Job job) { + List errors = job.errors(); + if (errors == null) { + return ""; + } + + return errors.stream() + .map(V3JobMapper::joinErrorDetails) + .collect(Collectors.joining(",")); + } + + private static String joinErrorDetails(V3Job.V3Error error) { + return String.join(" ", String.valueOf(error.code()), error.title(), error.detail()); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ListResponse.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ListResponse.java new file mode 100644 index 0000000000..5476df61e6 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ListResponse.java @@ -0,0 +1,44 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Collections; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a Cloud Controller v3 list response envelope: + * + *
+ * {
+ *   "pagination": { "total_results": N, "next": { "href": "..." } | null, ... },
+ *   "resources":  [ ... ]
+ * }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ListResponse(@JsonProperty("pagination") Pagination pagination, @JsonProperty("resources") List resources) { + + public List resources() { + return resources == null ? Collections.emptyList() : resources; + } + + public String nextPageHref() { + if (pagination == null || pagination.next() == null) { + return null; + } + + return pagination.next() + .href(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record Pagination(@JsonProperty("total_results") Integer totalResults, @JsonProperty("total_pages") Integer totalPages, + @JsonProperty("next") Link next, @JsonProperty("previous") Link previous) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record Link(@JsonProperty("href") String href) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Metadata.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Metadata.java new file mode 100644 index 0000000000..ef50b44d5c --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Metadata.java @@ -0,0 +1,24 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Collections; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of the CF v3 {@code metadata} object (labels + annotations) attached to most resources. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Metadata(@JsonProperty("labels") Map labels, + @JsonProperty("annotations") Map annotations) { + + public Map labels() { + return labels == null ? Collections.emptyMap() : labels; + } + + public Map annotations() { + return annotations == null ? Collections.emptyMap() : annotations; + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Organization.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Organization.java new file mode 100644 index 0000000000..e7b9a14729 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Organization.java @@ -0,0 +1,12 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 organization resource ({@code GET /v3/organizations/{guid}} / {@code GET /v3/organizations?...}). + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Organization(@JsonProperty("guid") String guid, @JsonProperty("name") String name, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt) { +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Package.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Package.java new file mode 100644 index 0000000000..c67ff92e0b --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Package.java @@ -0,0 +1,36 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 package resource ({@code GET/POST /v3/packages}, {@code GET /v3/apps/{guid}/packages}). + * + *
+ * { "guid": "...", "type": "bits|docker", "state": "AWAITING_UPLOAD|PROCESSING_UPLOAD|READY|FAILED|COPYING|EXPIRED",
+ *   "created_at": "...", "updated_at": "...",
+ *   "data": {
+ *     // bits:
+ *     "checksum": { "type": "sha256", "value": "..." }, "error": "...",
+ *     // docker:
+ *     "image": "...", "username": "...", "password": "..."
+ *   },
+ *   "relationships": { "app": { "data": { "guid": "..." } } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Package(@JsonProperty("guid") String guid, @JsonProperty("type") String type, @JsonProperty("state") String state, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("data") V3PackageData data) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3PackageData(@JsonProperty("checksum") V3Checksum checksum, @JsonProperty("error") String error, + @JsonProperty("image") String image, @JsonProperty("username") String username, + @JsonProperty("password") String password) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Checksum(@JsonProperty("type") String type, @JsonProperty("value") String value) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3PackageMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3PackageMapper.java new file mode 100644 index 0000000000..d0ba2e0616 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3PackageMapper.java @@ -0,0 +1,76 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.BitsData; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudPackage; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableBitsData; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudPackage; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDockerData; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Status; + +public final class V3PackageMapper { + + private V3PackageMapper() { + } + + public static CloudPackage toCloudPackage(V3Package resource) { + return ImmutableCloudPackage.builder() + .metadata(V3ResourceMappers.parseMetadata(resource.guid(), resource.createdAt(), + resource.updatedAt())) + .status(parseStatus(resource)) + .data(parseData(resource)) + .type(parseType(resource)) + .build(); + } + + private static Status parseStatus(V3Package resource) { + return resource.state() == null ? null : Status.valueOf(resource.state()); + } + + private static CloudPackage.Type parseType(V3Package resource) { + return resource.type() == null ? null : CloudPackage.Type.from(resource.type()); + } + + private static CloudPackage.PackageData parseData(V3Package resource) { + if (resource.type() != null && CloudPackage.Type.from(resource.type()) == CloudPackage.Type.BITS) { + return parseBitsData(resource.data()); + } + + return parseDockerData(resource.data()); + } + + private static CloudPackage.PackageData parseBitsData(V3Package.V3PackageData data) { + if (data == null) { + return ImmutableBitsData.builder() + .build(); + } + + return ImmutableBitsData.builder() + .checksum(parseBitsChecksum(data.checksum())) + .error(data.error()) + .build(); + } + + private static BitsData.Checksum + parseBitsChecksum(V3Package.V3Checksum checksum) { + if (checksum == null) { + return null; + } + + return ImmutableBitsData.ImmutableChecksum.builder() + .algorithm(checksum.type()) + .value(checksum.value()) + .build(); + } + + private static CloudPackage.PackageData parseDockerData(V3Package.V3PackageData data) { + if (data == null) { + return null; + } + return ImmutableDockerData.builder() + .image(data.image()) + .username(data.username()) + .password(data.password()) + .build(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Process.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Process.java new file mode 100644 index 0000000000..d010a950ee --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Process.java @@ -0,0 +1,51 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-models for the CF v3 process family of endpoints consumed by the client: + * + *
+ * // process
+ * { "guid": "...", "command": "...", "instances": 1, "memory_in_mb": 256, "disk_in_mb": 1024,
+ *   "health_check": { "type": "port|http|process", "data": { "timeout": N, "invocation_timeout": N, "endpoint": "...", "interval": N } },
+ *   "readiness_health_check": { "type": "process|http|port", "data": { "invocation_timeout": N, "endpoint": "...", "interval": N } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Process(@JsonProperty("guid") String guid, @JsonProperty("command") String command, + @JsonProperty("instances") Integer instances, @JsonProperty("memory_in_mb") Integer memoryInMb, + @JsonProperty("disk_in_mb") Integer diskInMb, @JsonProperty("health_check") V3HealthCheck healthCheck, + @JsonProperty("readiness_health_check") V3HealthCheck readinessHealthCheck) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3HealthCheck(@JsonProperty("type") String type, @JsonProperty("data") V3HealthCheckData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3HealthCheckData(@JsonProperty("timeout") Integer timeout, + @JsonProperty("invocation_timeout") Integer invocationTimeout, + @JsonProperty("endpoint") String endpoint, @JsonProperty("interval") Integer interval) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ProcessStats(@JsonProperty("resources") List resources) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ProcessStatsResource(@JsonProperty("index") Integer index, @JsonProperty("state") String state, + @JsonProperty("routable") String routable) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3AppFeature(@JsonProperty("name") String name, @JsonProperty("enabled") Boolean enabled) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3SshEnabled(@JsonProperty("enabled") Boolean enabled, @JsonProperty("reason") String reason) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ProcessMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ProcessMapper.java new file mode 100644 index 0000000000..49718c5fd4 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ProcessMapper.java @@ -0,0 +1,58 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; +import org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudProcess; + +public final class V3ProcessMapper { + + private V3ProcessMapper() { + } + + public static CloudProcess toCloudProcess(V3Process process) { + V3Process.V3HealthCheck healthCheck = process.healthCheck(); + V3Process.V3HealthCheck readinessHealthCheck = process.readinessHealthCheck(); + + Integer healthCheckTimeout = null; + String healthCheckHttpEndpoint = null; + Integer healthCheckInvocationTimeout = null; + Integer healthCheckInterval = null; + + if (healthCheck != null && healthCheck.data() != null) { + V3Process.V3HealthCheckData healthCheckData = healthCheck.data(); + healthCheckTimeout = healthCheckData.timeout(); + healthCheckInvocationTimeout = healthCheckData.invocationTimeout(); + healthCheckHttpEndpoint = healthCheckData.endpoint(); + healthCheckInterval = healthCheckData.interval(); + } + + Integer readinessHealthCheckInvocationTimeout = null; + String readinessHealthCheckHttpEndpoint = null; + Integer readinessHealthCheckInterval = null; + + if (readinessHealthCheck != null && readinessHealthCheck.data() != null) { + V3Process.V3HealthCheckData readinessHealthCheckData = readinessHealthCheck.data(); + readinessHealthCheckInvocationTimeout = readinessHealthCheckData.invocationTimeout(); + readinessHealthCheckHttpEndpoint = readinessHealthCheckData.endpoint(); + readinessHealthCheckInterval = readinessHealthCheckData.interval(); + } + + return ImmutableCloudProcess.builder() + .command(process.command()) + .instances(process.instances()) + .memoryInMb(process.memoryInMb()) + .diskInMb(process.diskInMb()) + .healthCheckType(HealthCheckType.valueOf(healthCheck.type() + .toUpperCase())) + .healthCheckHttpEndpoint(healthCheckHttpEndpoint) + .healthCheckTimeout(healthCheckTimeout) + .healthCheckInvocationTimeout(healthCheckInvocationTimeout) + .healthCheckInterval(healthCheckInterval) + .readinessHealthCheckType(readinessHealthCheck == null ? null : readinessHealthCheck.type()) + .readinessHealthCheckHttpEndpoint(readinessHealthCheckHttpEndpoint) + .readinessHealthCheckInvocationTimeout(readinessHealthCheckInvocationTimeout) + .readinessHealthCheckInterval(readinessHealthCheckInterval) + .build(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ResourceMappers.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ResourceMappers.java new file mode 100644 index 0000000000..e8500919ee --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ResourceMappers.java @@ -0,0 +1,68 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeParseException; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class V3ResourceMappers { + + private static final Logger LOGGER = LoggerFactory.getLogger(V3ResourceMappers.class); + + private V3ResourceMappers() { + } + + public static CloudMetadata parseMetadata(String guid, String createdAt, String updatedAt) { + return ImmutableCloudMetadata.builder() + .guid(parseNullableGuid(guid)) + .createdAt(parseNullableDate(createdAt)) + .updatedAt(parseNullableDate(updatedAt)) + .build(); + } + + public static Metadata toV3Metadata(V3Metadata metadata) { + if (metadata == null) { + return null; + } + + return Metadata.builder() + .labels(metadata.labels()) + .annotations(metadata.annotations()) + .build(); + } + + public static UUID parseNullableGuid(String guid) { + if (guid == null) { + return null; + } + + try { + return UUID.fromString(guid); + } catch (IllegalArgumentException e) { + LOGGER.warn("Could not parse GUID string: \"{}\"", guid, e); + return null; + } + } + + public static LocalDateTime parseNullableDate(String date) { + if (date == null) { + return null; + } + + try { + // CF v3 timestamps are ISO-8601 with offset, e.g. 2026-08-04T10:15:30Z + return OffsetDateTime.parse(date) + .toLocalDateTime(); + } catch (DateTimeParseException e) { + LOGGER.warn("Could not parse date string: \"{}\"", date, e); + return null; + } + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Role.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Role.java new file mode 100644 index 0000000000..f192169500 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Role.java @@ -0,0 +1,19 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 role resource ({@code GET /v3/roles}). + * + *
+ * { "guid": "...", "created_at": "...", "updated_at": "...",
+ *   "type": "organization_auditor|organization_billing_manager|organization_manager|organization_user|space_auditor|space_developer|space_manager",
+ *   "relationships": { "user": { "data": { "guid": "..." } }, "space": { "data": { "guid": "..." } | null },
+ *                      "organization": { "data": { "guid": "..." } | null } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Role(@JsonProperty("guid") String guid, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("type") String type) { +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3RoleMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3RoleMapper.java new file mode 100644 index 0000000000..7e5d4372a1 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3RoleMapper.java @@ -0,0 +1,14 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.UserRole; + +public final class V3RoleMapper { + + private V3RoleMapper() { + } + + public static UserRole toUserRole(V3Role role) { + return UserRole.fromRoleType(role.type()); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Route.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Route.java new file mode 100644 index 0000000000..b29aa562d7 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Route.java @@ -0,0 +1,54 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Collections; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 route resource ({@code GET/POST /v3/routes}, {@code GET /v3/apps/{guid}/routes}). + * + *
+ * { "guid": "...", "host": "...", "path": "...", "port": 8080, "url": "host.domain/path",
+ *   "created_at": "...", "updated_at": "...",
+ *   "destinations": [ { "guid": "...", "app": { "guid": "..." }, "port": 8080, "weight": 1, "protocol": "http1" } ],
+ *   "metadata": { "labels": {...}, "annotations": {...} },
+ *   "relationships": { "space": { "data": { "guid": "..." } }, "domain": { "data": { "guid": "..." } } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Route(@JsonProperty("guid") String guid, @JsonProperty("host") String host, @JsonProperty("path") String path, + @JsonProperty("port") Integer port, @JsonProperty("url") String url, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("destinations") List destinations, @JsonProperty("metadata") V3Metadata metadata, + @JsonProperty("relationships") V3RouteRelationships relationships) { + + public List destinations() { + return destinations == null ? Collections.emptyList() : destinations; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Destination(@JsonProperty("guid") String guid, @JsonProperty("app") V3DestinationApp app, + @JsonProperty("port") Integer port, @JsonProperty("weight") Integer weight, + @JsonProperty("protocol") String protocol) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3DestinationApp(@JsonProperty("guid") String guid) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RouteRelationships(@JsonProperty("space") V3ToOneRelationship space, + @JsonProperty("domain") V3ToOneRelationship domain) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudRoute.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3RouteMapper.java similarity index 50% rename from multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudRoute.java rename to multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3RouteMapper.java index e2ae758814..6c8691d6e4 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudRoute.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3RouteMapper.java @@ -1,14 +1,10 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.routes.Route; -import org.immutables.value.Value; - -import org.cloudfoundry.multiapps.controller.client.facade.Nullable; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudRoute; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudDomain; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; @@ -16,79 +12,80 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableRouteDestination; import org.cloudfoundry.multiapps.controller.client.facade.domain.RouteDestination; -@Value.Immutable -public abstract class RawCloudRoute extends RawCloudEntity { +public final class V3RouteMapper { - @Value.Parameter - public abstract Route getRoute(); + private V3RouteMapper() { + } - @Nullable - public abstract UUID getApplicationGuid(); + public static CloudRoute toCloudRoute(V3Route route) { + return toCloudRoute(route, null); + } + + public static CloudRoute toCloudRoute(V3Route route, UUID applicationGuid) { + List destinations = mapDestinations(route); + String domainGuid = route.relationships() + .domain() + .data() + .guid(); - @Override - public CloudRoute derive() { - Route route = getRoute(); - List destinations = mapDestinations(); - String domainGuid = route.getRelationships() - .getDomain() - .getData() - .getId(); return ImmutableCloudRoute.builder() - .metadata(parseResourceMetadata(route)) - .appsUsingRoute(route.getDestinations() + .metadata(V3ResourceMappers.parseMetadata(route.guid(), route.createdAt(), route.updatedAt())) + .appsUsingRoute(route.destinations() .size()) - .host(route.getHost()) - .port(route.getPort()) + .host(route.host()) + .port(route.port()) .domain(ImmutableCloudDomain.builder() .name(computeDomain(route)) .metadata(ImmutableCloudMetadata.of(UUID.fromString(domainGuid))) .build()) - .path(route.getPath()) - .url(route.getUrl()) + .path(route.path()) + .url(route.url()) .destinations(destinations) - .requestedProtocol(computeRequestedProtocol(destinations)) + .requestedProtocol(computeRequestedProtocol(destinations, applicationGuid)) .build(); } - private static String computeDomain(Route route) { - String domain = route.getUrl(); - if (!route.getHost() - .isEmpty()) { - domain = domain.substring(route.getHost() - .length() - + 1); + private static String computeDomain(V3Route route) { + String domain = route.url(); + String host = route.host() == null ? "" : route.host(); + String path = route.path() == null ? "" : route.path(); + + if (!host.isEmpty()) { + domain = domain.substring(host.length() + 1); } - if (!route.getPath() - .isEmpty()) { + + if (!path.isEmpty()) { domain = domain.substring(0, domain.indexOf('/')); } - if (route.getPort() != null) { + + if (route.port() != null) { domain = domain.substring(0, domain.indexOf(':')); } + return domain; } - private List mapDestinations() { - return getRoute().getDestinations() - .stream() - .map(destination -> ImmutableRouteDestination.builder() - .metadata(ImmutableCloudMetadata.builder() - .guid(UUID.fromString(destination.getDestinationId())) - .build()) - .applicationGuid(UUID.fromString(destination.getApplication() - .getApplicationId())) - .port(destination.getPort()) - .weight(destination.getWeight()) - .protocol(destination.getProtocol()) - .build()) - .collect(Collectors.toList()); + private static List mapDestinations(V3Route route) { + return route.destinations() + .stream() + .map(destination -> ImmutableRouteDestination.builder() + .metadata(ImmutableCloudMetadata.builder() + .guid(UUID.fromString(destination.guid())) + .build()) + .applicationGuid(UUID.fromString(destination.app() + .guid())) + .port(destination.port()) + .weight(destination.weight()) + .protocol(destination.protocol()) + .build()) + .collect(Collectors.toList()); } - private String computeRequestedProtocol(List destinations) { - UUID applicationGuid = getApplicationGuid(); + private static String computeRequestedProtocol(List destinations, UUID applicationGuid) { if (applicationGuid == null) { return null; } + return destinations.stream() .filter(destination -> Objects.equals(destination.getApplicationGuid(), applicationGuid)) .findFirst() diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBinding.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBinding.java new file mode 100644 index 0000000000..9c24571233 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBinding.java @@ -0,0 +1,46 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 service credential binding resource ({@code GET/POST/PATCH/DELETE + * /v3/service_credential_bindings}). A binding is either of {@code type=app} (an application bound to a service instance) or + * {@code type=key} (a service key); the {@code relationships.app} is absent for keys. + * + *
+ * { "guid": "...", "name": "...", "type": "app|key", "created_at": "...", "updated_at": "...",
+ *   "last_operation": { "type": "create|delete", "state": "initial|in progress|succeeded|failed",
+ *                       "description": "...", "created_at": "...", "updated_at": "..." },
+ *   "metadata": { "labels": {...}, "annotations": {...} },
+ *   "relationships": { "app": { "data": { "guid": "..." } | null },
+ *                      "service_instance": { "data": { "guid": "..." } } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ServiceBinding(@JsonProperty("guid") String guid, @JsonProperty("name") String name, @JsonProperty("type") String type, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("last_operation") V3LastOperation lastOperation, + @JsonProperty("metadata") V3Metadata metadata, + @JsonProperty("relationships") V3Relationships relationships) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3LastOperation(@JsonProperty("type") String type, @JsonProperty("state") String state, + @JsonProperty("description") String description, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Relationships(@JsonProperty("app") V3ToOneRelationship application, + @JsonProperty("service_instance") V3ToOneRelationship serviceInstance) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBindingMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBindingMapper.java new file mode 100644 index 0000000000..96a3bf82c6 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBindingMapper.java @@ -0,0 +1,65 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableServiceCredentialBindingOperation; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; + +public final class V3ServiceBindingMapper { + + private V3ServiceBindingMapper() { + } + + public static CloudServiceBinding toCloudServiceBinding(V3ServiceBinding binding) { + return ImmutableCloudServiceBinding.builder() + .metadata(V3ResourceMappers.parseMetadata(binding.guid(), binding.createdAt(), + binding.updatedAt())) + .applicationGuid(parseApplicationGuid(binding)) + .serviceInstanceGuid(parseServiceInstanceGuid(binding)) + .serviceBindingOperation(parseServiceBindingOperation(binding.lastOperation())) + .build(); + } + + private static UUID parseApplicationGuid(V3ServiceBinding binding) { + V3ServiceBinding.V3ToOneRelationship application = binding.relationships() == null ? null + : binding.relationships() + .application(); + + if (application == null || application.data() == null) { + return null; + } + + return V3ResourceMappers.parseNullableGuid(application.data() + .guid()); + } + + private static UUID parseServiceInstanceGuid(V3ServiceBinding binding) { + return UUID.fromString(binding.relationships() + .serviceInstance() + .data() + .guid()); + } + + private static ServiceCredentialBindingOperation parseServiceBindingOperation(V3ServiceBinding.V3LastOperation lastOperation) { + if (lastOperation == null) { + return null; + } + + return ImmutableServiceCredentialBindingOperation.builder() + .type(ServiceCredentialBindingOperation.Type.fromString(lastOperation.type())) + .state(ServiceCredentialBindingOperation.State.fromString(lastOperation.state())) + .description(lastOperation.description()) + .createdAt(parseDate(lastOperation.createdAt())) + .updatedAt(parseDate(lastOperation.updatedAt())) + .build(); + } + + private static LocalDateTime parseDate(String date) { + return date == null ? null : LocalDateTime.parse(date, DateTimeFormatter.ISO_DATE_TIME); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBroker.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBroker.java new file mode 100644 index 0000000000..2fe6d5d6d8 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBroker.java @@ -0,0 +1,33 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 service broker resource ({@code GET/POST/PATCH /v3/service_brokers}). + * + *
+ * { "guid": "...", "name": "...", "url": "...", "created_at": "...", "updated_at": "...",
+ *   "metadata": { "labels": {...}, "annotations": {...} },
+ *   "relationships": { "space": { "data": { "guid": "..." } | null } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ServiceBroker(@JsonProperty("guid") String guid, @JsonProperty("name") String name, @JsonProperty("url") String url, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("metadata") V3Metadata metadata, + @JsonProperty("relationships") V3Relationships relationships) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Relationships(@JsonProperty("space") V3ToOneRelationship space) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBrokerMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBrokerMapper.java new file mode 100644 index 0000000000..8b2b1cff2b --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceBrokerMapper.java @@ -0,0 +1,31 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Optional; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBroker; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBroker; + +public final class V3ServiceBrokerMapper { + + private V3ServiceBrokerMapper() { + } + + public static CloudServiceBroker toCloudServiceBroker(V3ServiceBroker serviceBroker) { + return ImmutableCloudServiceBroker.builder() + .metadata(V3ResourceMappers.parseMetadata(serviceBroker.guid(), serviceBroker.createdAt(), + serviceBroker.updatedAt())) + .name(serviceBroker.name()) + .url(serviceBroker.url()) + .spaceGuid(getSpaceGuid(serviceBroker)) + .build(); + } + + private static String getSpaceGuid(V3ServiceBroker serviceBroker) { + return Optional.ofNullable(serviceBroker.relationships()) + .map(V3ServiceBroker.V3Relationships::space) + .map(V3ServiceBroker.V3ToOneRelationship::data) + .map(V3ServiceBroker.V3RelationshipData::guid) + .orElse(null); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceInstance.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceInstance.java new file mode 100644 index 0000000000..1f01ad0c34 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceInstance.java @@ -0,0 +1,46 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 service instance resource ({@code GET/POST/PATCH /v3/service_instances}). + * + *
+ * { "guid": "...", "name": "...", "type": "managed|user-provided",
+ *   "created_at": "...", "updated_at": "...",
+ *   "tags": [...], "syslog_drain_url": "...",
+ *   "last_operation": { "type": "create|update|delete", "state": "succeeded|failed|in progress|initial", "description": "..." },
+ *   "metadata": { "labels": {...}, "annotations": {...} },
+ *   "relationships": { "space": { "data": { "guid": "..." } }, "service_plan": { "data": { "guid": "..." } } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ServiceInstance(@JsonProperty("guid") String guid, @JsonProperty("name") String name, @JsonProperty("type") String type, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("tags") List tags, @JsonProperty("syslog_drain_url") String syslogDrainUrl, + @JsonProperty("last_operation") V3LastOperation lastOperation, + @JsonProperty("metadata") V3Metadata metadata, + @JsonProperty("relationships") V3Relationships relationships) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3LastOperation(@JsonProperty("type") String type, @JsonProperty("state") String state, + @JsonProperty("description") String description) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Relationships(@JsonProperty("space") V3ToOneRelationship space, + @JsonProperty("service_plan") V3ToOneRelationship servicePlan) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceInstanceMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceInstanceMapper.java new file mode 100644 index 0000000000..2746c0cb29 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceInstanceMapper.java @@ -0,0 +1,68 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation; + +public final class V3ServiceInstanceMapper { + + private V3ServiceInstanceMapper() { + } + + public static CloudServiceInstance toCloudServiceInstance(V3ServiceInstance serviceInstance, String planName, String labelName) { + return ImmutableCloudServiceInstance.builder() + .metadata(V3ResourceMappers.parseMetadata(serviceInstance.guid(), + serviceInstance.createdAt(), + serviceInstance.updatedAt())) + .v3Metadata(V3ResourceMappers.toV3Metadata(serviceInstance.metadata())) + .name(serviceInstance.name()) + .plan(planName) + .label(labelName) + .type(parseType(serviceInstance.type())) + .tags(serviceInstance.tags() == null ? java.util.Collections.emptyList() + : serviceInstance.tags()) + .lastOperation(parseLastOperation(serviceInstance.lastOperation())) + .syslogDrainUrl(serviceInstance.syslogDrainUrl()) + .build(); + } + + public static CloudServiceInstance toCloudServiceInstanceWithoutAuxiliaryContent(V3ServiceInstance serviceInstance) { + return ImmutableCloudServiceInstance.builder() + .metadata(V3ResourceMappers.parseMetadata(serviceInstance.guid(), + serviceInstance.createdAt(), + serviceInstance.updatedAt())) + .v3Metadata(V3ResourceMappers.toV3Metadata(serviceInstance.metadata())) + .name(serviceInstance.name()) + .tags(serviceInstance.tags() == null ? java.util.Collections.emptyList() + : serviceInstance.tags()) + .build(); + } + + private static ServiceInstanceType parseType(String type) { + if (type == null) { + return null; + } + + for (ServiceInstanceType serviceInstanceType : ServiceInstanceType.values()) { + if (serviceInstanceType.getValue() + .equals(type)) { + return serviceInstanceType; + } + } + + return null; + } + + private static ServiceOperation parseLastOperation(V3ServiceInstance.V3LastOperation lastOperation) { + if (lastOperation == null || lastOperation.type() == null || lastOperation.state() == null) { + return null; + } + + ServiceOperation.Type type = ServiceOperation.Type.fromString(lastOperation.type()); + ServiceOperation.State state = ServiceOperation.State.fromString(lastOperation.state()); + + return new ServiceOperation(type, lastOperation.description(), state); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceKeyDetails.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceKeyDetails.java new file mode 100644 index 0000000000..2d1df8b410 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceKeyDetails.java @@ -0,0 +1,25 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Collections; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of the CF v3 service credential binding details response + * ({@code GET /v3/service_credential_bindings/{guid}/details}), which is where the actual {@code credentials} of a service key live. + * + *
+ * { "credentials": { ... }, "syslog_drain_url": "...", "volume_mounts": [ ... ] }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ServiceKeyDetails(@JsonProperty("credentials") Map credentials, + @JsonProperty("syslog_drain_url") String syslogDrainUrl) { + + public Map credentials() { + return credentials == null ? Collections.emptyMap() : credentials; + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceKeyMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceKeyMapper.java new file mode 100644 index 0000000000..23a6f16816 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceKeyMapper.java @@ -0,0 +1,46 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableServiceCredentialBindingOperation; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; + +public final class V3ServiceKeyMapper { + + private V3ServiceKeyMapper() { + } + + public static CloudServiceKey toCloudServiceKey(V3ServiceBinding key, CloudServiceInstance serviceInstance) { + return toCloudServiceKey(key, serviceInstance, null); + } + + public static CloudServiceKey toCloudServiceKey(V3ServiceBinding key, CloudServiceInstance serviceInstance, + Map credentials) { + return ImmutableCloudServiceKey.builder() + .metadata(V3ResourceMappers.parseMetadata(key.guid(), key.createdAt(), key.updatedAt())) + .v3Metadata(V3ResourceMappers.toV3Metadata(key.metadata())) + .name(key.name()) + .credentials(credentials) + .serviceInstance(serviceInstance) + .serviceKeyOperation(parseServiceKeyOperation(key.lastOperation())) + .build(); + } + + private static ServiceCredentialBindingOperation parseServiceKeyOperation(V3ServiceBinding.V3LastOperation lastOperation) { + if (lastOperation == null) { + return null; + } + + return ImmutableServiceCredentialBindingOperation.builder() + .type(ServiceCredentialBindingOperation.Type.fromString(lastOperation.type())) + .state(ServiceCredentialBindingOperation.State.fromString(lastOperation.state())) + .description(lastOperation.description()) + .createdAt(V3ResourceMappers.parseNullableDate(lastOperation.createdAt())) + .updatedAt(V3ResourceMappers.parseNullableDate(lastOperation.updatedAt())) + .build(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceOffering.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceOffering.java new file mode 100644 index 0000000000..f0e64df11f --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceOffering.java @@ -0,0 +1,54 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 service offering resource ({@code GET /v3/service_offerings}). + * + *
+ * { "guid": "...", "name": "...", "description": "...", "available": true, "shareable": true,
+ *   "documentation_url": "...", "created_at": "...", "updated_at": "...",
+ *   "broker_catalog": { "id": "...", "metadata": {...}, "features": { "bindable": true, ... } },
+ *   "relationships": { "service_broker": { "data": { "guid": "..." } } },
+ *   "metadata": { "labels": {...}, "annotations": {...} } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ServiceOffering(@JsonProperty("guid") String guid, @JsonProperty("name") String name, + @JsonProperty("description") String description, @JsonProperty("available") Boolean available, + @JsonProperty("shareable") Boolean shareable, + @JsonProperty("documentation_url") String documentationUrl, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("broker_catalog") V3BrokerCatalog brokerCatalog, + @JsonProperty("relationships") V3Relationships relationships, + @JsonProperty("metadata") V3Metadata metadata) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3BrokerCatalog(@JsonProperty("id") String id, @JsonProperty("metadata") Map metadata, + @JsonProperty("features") V3Features features) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Features(@JsonProperty("bindable") Boolean bindable, + @JsonProperty("allow_context_updates") Boolean allowContextUpdates, + @JsonProperty("bindings_retrievable") Boolean bindingsRetrievable, + @JsonProperty("instances_retrievable") Boolean instancesRetrievable, + @JsonProperty("plan_updateable") Boolean planUpdateable) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Relationships(@JsonProperty("service_broker") V3ToOneRelationship serviceBroker) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceOfferingMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceOfferingMapper.java new file mode 100644 index 0000000000..d98d5f2b3f --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServiceOfferingMapper.java @@ -0,0 +1,61 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.List; +import java.util.Map; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceOffering; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceOffering; + +public final class V3ServiceOfferingMapper { + + private V3ServiceOfferingMapper() { + } + + public static CloudServiceOffering toCloudServiceOffering(V3ServiceOffering serviceOffering, List servicePlans) { + return ImmutableCloudServiceOffering.builder() + .metadata(V3ResourceMappers.parseMetadata(serviceOffering.guid(), + serviceOffering.createdAt(), + serviceOffering.updatedAt())) + .name(serviceOffering.name()) + .isAvailable(serviceOffering.available()) + .isBindable(extractBindable(serviceOffering.brokerCatalog())) + .description(serviceOffering.description()) + .isShareable(serviceOffering.shareable()) + .extra(extractBrokerCatalogMetadata(serviceOffering.brokerCatalog())) + .docUrl(serviceOffering.documentationUrl()) + .brokerId(extractBrokerId(serviceOffering.relationships())) + .uniqueId(extractBrokerCatalogId(serviceOffering.brokerCatalog())) + .servicePlans(servicePlans) + .build(); + } + + private static Boolean extractBindable(V3ServiceOffering.V3BrokerCatalog brokerCatalog) { + if (brokerCatalog == null || brokerCatalog.features() == null) { + return null; + } + + return brokerCatalog.features() + .bindable(); + } + + private static Map extractBrokerCatalogMetadata(V3ServiceOffering.V3BrokerCatalog brokerCatalog) { + return brokerCatalog == null ? null : brokerCatalog.metadata(); + } + + private static String extractBrokerCatalogId(V3ServiceOffering.V3BrokerCatalog brokerCatalog) { + return brokerCatalog == null ? null : brokerCatalog.id(); + } + + private static String extractBrokerId(V3ServiceOffering.V3Relationships relationships) { + if (relationships == null || relationships.serviceBroker() == null || relationships.serviceBroker() + .data() == null) { + return null; + } + + return relationships.serviceBroker() + .data() + .guid(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServicePlan.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServicePlan.java new file mode 100644 index 0000000000..f7da724b1c --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServicePlan.java @@ -0,0 +1,43 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 service plan resource ({@code GET /v3/service_plans}). + * + *
+ * { "guid": "...", "name": "...", "description": "...", "free": true|false,
+ *   "visibility_type": "public|admin|organization|space",
+ *   "created_at": "...", "updated_at": "...",
+ *   "broker_catalog": { "id": "...", "metadata": { ... } },
+ *   "relationships": { "service_offering": { "data": { "guid": "..." } } },
+ *   "metadata": { "labels": {...}, "annotations": {...} } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3ServicePlan(@JsonProperty("guid") String guid, @JsonProperty("name") String name, + @JsonProperty("description") String description, @JsonProperty("free") Boolean free, + @JsonProperty("visibility_type") String visibilityType, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("broker_catalog") V3BrokerCatalog brokerCatalog, + @JsonProperty("relationships") V3Relationships relationships, @JsonProperty("metadata") V3Metadata metadata) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3BrokerCatalog(@JsonProperty("id") String id, @JsonProperty("metadata") Map metadata) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Relationships(@JsonProperty("service_offering") V3ToOneRelationship serviceOffering) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOneRelationship(@JsonProperty("data") V3RelationshipData data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3RelationshipData(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServicePlanMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServicePlanMapper.java new file mode 100644 index 0000000000..9b6711fa94 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3ServicePlanMapper.java @@ -0,0 +1,51 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServicePlan; + +public final class V3ServicePlanMapper { + + private static final String PUBLIC_VISIBILITY = "public"; + + private V3ServicePlanMapper() { + } + + public static CloudServicePlan toCloudServicePlan(V3ServicePlan servicePlan) { + return ImmutableCloudServicePlan.builder() + .metadata(V3ResourceMappers.parseMetadata(servicePlan.guid(), servicePlan.createdAt(), + servicePlan.updatedAt())) + .v3Metadata(V3ResourceMappers.toV3Metadata(servicePlan.metadata())) + .name(servicePlan.name()) + .description(servicePlan.description()) + .extra(extractBrokerCatalogMetadata(servicePlan.brokerCatalog())) + .uniqueId(extractBrokerCatalogId(servicePlan.brokerCatalog())) + .serviceOfferingId(extractServiceOfferingId(servicePlan.relationships())) + .isPublic(isPublic(servicePlan.visibilityType())) + .isFree(servicePlan.free()) + .build(); + } + + private static java.util.Map extractBrokerCatalogMetadata(V3ServicePlan.V3BrokerCatalog brokerCatalog) { + return brokerCatalog == null ? null : brokerCatalog.metadata(); + } + + private static String extractBrokerCatalogId(V3ServicePlan.V3BrokerCatalog brokerCatalog) { + return brokerCatalog == null ? null : brokerCatalog.id(); + } + + private static String extractServiceOfferingId(V3ServicePlan.V3Relationships relationships) { + if (relationships == null || relationships.serviceOffering() == null || relationships.serviceOffering() + .data() == null) { + return null; + } + + return relationships.serviceOffering() + .data() + .guid(); + } + + private static Boolean isPublic(String visibilityType) { + return visibilityType == null ? null : PUBLIC_VISIBILITY.equals(visibilityType); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Space.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Space.java new file mode 100644 index 0000000000..70e8c3f4bd --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Space.java @@ -0,0 +1,41 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 space resource ({@code GET /v3/spaces/{guid}} / {@code GET /v3/spaces?...}). + * + *
+ * { "guid": "...", "name": "...", "created_at": "...", "updated_at": "...",
+ *   "relationships": { "organization": { "data": { "guid": "..." } } } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Space(@JsonProperty("guid") String guid, @JsonProperty("name") String name, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("relationships") V3SpaceRelationships relationships) { + + public String organizationGuid() { + if (relationships == null || relationships.organization() == null || relationships.organization() + .data() == null) { + return null; + } + + return relationships.organization() + .data() + .guid(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3SpaceRelationships(@JsonProperty("organization") V3ToOne organization) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3ToOne(@JsonProperty("data") V3Data data) { + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Data(@JsonProperty("guid") String guid) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Stack.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Stack.java new file mode 100644 index 0000000000..d1ba56ffe3 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Stack.java @@ -0,0 +1,20 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 stack resource ({@code GET /v3/stacks}). + * + *
+ * { "guid": "...", "name": "cflinuxfs4", "description": "...",
+ *   "created_at": "...", "updated_at": "...",
+ *   "metadata": { "labels": {...}, "annotations": {...} } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Stack(@JsonProperty("guid") String guid, @JsonProperty("name") String name, + @JsonProperty("description") String description, @JsonProperty("created_at") String createdAt, + @JsonProperty("updated_at") String updatedAt, @JsonProperty("metadata") V3Metadata metadata) { + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3StackMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3StackMapper.java new file mode 100644 index 0000000000..c821ec9104 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3StackMapper.java @@ -0,0 +1,19 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudStack; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudStack; + +public final class V3StackMapper { + + private V3StackMapper() { + } + + public static CloudStack toCloudStack(V3Stack stack) { + return ImmutableCloudStack.builder() + .metadata(V3ResourceMappers.parseMetadata(stack.guid(), stack.createdAt(), stack.updatedAt())) + .name(stack.name()) + .description(stack.description()) + .build(); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Task.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Task.java new file mode 100644 index 0000000000..bddbd82ec6 --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3Task.java @@ -0,0 +1,28 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Thin Jackson wire-model of a CF v3 task resource ({@code GET/POST /v3/tasks}, {@code POST /v3/apps/{guid}/tasks}). + * + *
+ * { "guid": "...", "name": "...", "command": "...", "state": "PENDING|RUNNING|SUCCEEDED|CANCELING|FAILED",
+ *   "memory_in_mb": 256, "disk_in_mb": 1024,
+ *   "result": { "failure_reason": "..." | null },
+ *   "created_at": "...", "updated_at": "...",
+ *   "metadata": { "labels": {...}, "annotations": {...} } }
+ * 
+ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record V3Task(@JsonProperty("guid") String guid, @JsonProperty("name") String name, @JsonProperty("command") String command, + @JsonProperty("state") String state, @JsonProperty("memory_in_mb") Integer memoryInMb, + @JsonProperty("disk_in_mb") Integer diskInMb, @JsonProperty("result") V3Result result, + @JsonProperty("created_at") String createdAt, @JsonProperty("updated_at") String updatedAt, + @JsonProperty("metadata") V3Metadata metadata) { + + @JsonIgnoreProperties(ignoreUnknown = true) + public record V3Result(@JsonProperty("failure_reason") String failureReason) { + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3TaskMapper.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3TaskMapper.java new file mode 100644 index 0000000000..bde50c807e --- /dev/null +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/resources/V3TaskMapper.java @@ -0,0 +1,42 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest.resources; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudTask; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudTask; + +public final class V3TaskMapper { + + private V3TaskMapper() { + } + + public static CloudTask toCloudTask(V3Task task) { + return ImmutableCloudTask.builder() + .metadata(V3ResourceMappers.parseMetadata(task.guid(), task.createdAt(), task.updatedAt())) + .name(task.name()) + .command(task.command()) + .limits(parseLimits(task)) + .result(parseResult(task)) + .state(parseState(task.state())) + .build(); + } + + private static CloudTask.Result parseResult(V3Task task) { + if (task.result() == null) { + return null; + } + + return ImmutableCloudTask.ImmutableResult.of(task.result() + .failureReason()); + } + + private static CloudTask.Limits parseLimits(V3Task task) { + return ImmutableCloudTask.ImmutableLimits.builder() + .disk(task.diskInMb()) + .memory(task.memoryInMb()) + .build(); + } + + private static CloudTask.State parseState(String state) { + return state == null ? null : CloudTask.State.valueOf(state); + } + +} diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/util/JobV3Util.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/util/JobV3Util.java deleted file mode 100644 index e7e293c426..0000000000 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/util/JobV3Util.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.util; - -import java.time.Duration; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; - -import org.cloudfoundry.client.CloudFoundryClient; -import org.cloudfoundry.client.v3.ClientV3Exception; -import org.cloudfoundry.client.v3.Error; -import org.cloudfoundry.client.v3.jobs.GetJobRequest; -import org.cloudfoundry.client.v3.jobs.GetJobResponse; -import org.cloudfoundry.client.v3.jobs.Job; -import org.cloudfoundry.client.v3.jobs.JobState; -import org.cloudfoundry.util.DelayUtils; - -import reactor.core.publisher.Mono; - -public class JobV3Util { - - private JobV3Util() { - } - - private static final Set FINAL_STATES = EnumSet.of(JobState.COMPLETE, JobState.FAILED); - - public static Mono waitForCompletion(CloudFoundryClient cloudFoundryClient, Duration completionTimeout, String jobId) { - return requestJobV3(cloudFoundryClient, jobId).filter(job -> FINAL_STATES.contains(job.getState())) - .repeatWhenEmpty(DelayUtils.exponentialBackOff(Duration.ofSeconds(1), - Duration.ofSeconds(15), - completionTimeout)) - .filter(job -> JobState.FAILED == job.getState()) - .flatMap(JobV3Util::getError); - } - - private static Mono requestJobV3(CloudFoundryClient cloudFoundryClient, String jobId) { - return cloudFoundryClient.jobsV3() - .get(GetJobRequest.builder() - .jobId(jobId) - .build()); - } - - private static Mono getError(Job job) { - List errors = job.getErrors(); - // Status code must be set, otherwise it will throw NPE during getStatusCode() invocation - return Mono.error(new ClientV3Exception(200, errors)); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ApplicationsCloudControllerClientIntegrationTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ApplicationsCloudControllerClientIntegrationTest.java index 4cc72cbed1..95ca5e50bd 100644 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ApplicationsCloudControllerClientIntegrationTest.java +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ApplicationsCloudControllerClientIntegrationTest.java @@ -1,7 +1,7 @@ package org.cloudfoundry.multiapps.controller.client.facade; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.processes.HealthCheckType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; @@ -157,27 +157,12 @@ void updateApplicationHealthcheckType() { String applicationName = "test-application-16"; try { createAndVerifyDefaultApplication(applicationName); + client.updateApplicationStaging(applicationName, ImmutableStaging.builder() + .healthCheckType(HealthCheckType.PROCESS.getValue()) + .build()); UUID applicationGuid = client.getApplicationGuid(applicationName); - var processResponse = delegate.applicationsV3() - .getProcess(org.cloudfoundry.client.v3.applications.GetApplicationProcessRequest.builder() - .applicationId( - applicationGuid.toString()) - .type("web") - .build()) - .block(); - delegate.processes() - .update(org.cloudfoundry.client.v3.processes.UpdateProcessRequest.builder() - .processId(processResponse.getId()) - .healthCheck( - org.cloudfoundry.client.v3.processes.HealthCheck.builder() - .type( - HealthCheckType.PROCESS) - .build()) - .build()) - .block(); CloudProcess cloudProcess = client.getApplicationProcess(applicationGuid); - assertEquals(org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType.PROCESS, - cloudProcess.getHealthCheckType()); + assertEquals(HealthCheckType.PROCESS, cloudProcess.getHealthCheckType()); } catch (Exception e) { fail(e); } finally { diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientIntegrationTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientIntegrationTest.java index b394252229..9c7ea635fe 100644 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientIntegrationTest.java +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientIntegrationTest.java @@ -7,15 +7,16 @@ import java.util.HashMap; import java.util.Map; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableCloudFoundryClientFactory; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableLifecycle; import org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle; import org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType; import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; +import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; +import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudControllerRestClientFactory; import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudSpaceClient; +import org.cloudfoundry.multiapps.controller.client.facade.rest.ImmutableCloudControllerRestClientFactory; import org.cloudfoundry.multiapps.controller.client.facade.util.RestUtil; -import org.cloudfoundry.client.CloudFoundryClient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -30,7 +31,6 @@ abstract class CloudControllerClientIntegrationTest { protected static final String DEFAULT_STACK = "cflinuxfs4"; protected static CloudControllerClient client; - protected static CloudFoundryClient delegate; protected static CloudSpace target; @BeforeAll @@ -39,14 +39,14 @@ static void login() throws MalformedURLException { CloudCredentials credentials = getCloudCredentials(); URL apiUrl = URI.create(ITVariable.CF_API.getValue()) .toURL(); - var clientFactory = ImmutableCloudFoundryClientFactory.builder() - .build(); - var oauthClient = new RestUtil().createOAuthClientByControllerUrl(apiUrl, true); + CloudControllerRestClientFactory clientFactory = ImmutableCloudControllerRestClientFactory.builder() + .shouldTrustSelfSignedCertificates(true) + .build(); + OAuthClient oauthClient = new RestUtil().createOAuthClientByControllerUrl(apiUrl, true); oauthClient.init(credentials); CloudSpaceClient spaceClient = clientFactory.createSpaceClient(apiUrl, oauthClient, Collections.emptyMap()); target = spaceClient.getSpace(ITVariable.ORG.getValue(), ITVariable.SPACE.getValue()); client = new CloudControllerClientImpl(apiUrl, credentials, target, true); - delegate = clientFactory.createClient(apiUrl, oauthClient, Collections.emptyMap()); } @BeforeEach @@ -68,7 +68,7 @@ protected Lifecycle createLifecycle(Staging staging) { .build(); } - var data = new HashMap(); + HashMap data = new HashMap(); data.put("buildpacks", staging.getBuildpacks()); data.put("stack", DEFAULT_STACK); diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ServicesCloudControllerClientIntegrationTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ServicesCloudControllerClientIntegrationTest.java index c0de6f9023..0cc22fe179 100644 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ServicesCloudControllerClientIntegrationTest.java +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/ServicesCloudControllerClientIntegrationTest.java @@ -23,8 +23,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.jobs.JobState; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/OAuthTokenProviderTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/OAuthTokenProviderTest.java deleted file mode 100644 index f6131944b1..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/OAuthTokenProviderTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo; -import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; -import org.cloudfoundry.reactor.ConnectionContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import reactor.core.publisher.Mono; - -class OAuthTokenProviderTest { - - @Mock - private OAuthClient oAuthClient; - @Mock - private OAuth2AccessTokenWithAdditionalInfo token; - @Mock - private ConnectionContext connectionContext; - - private OAuthTokenProvider provider; - - @BeforeEach - void setUp() throws Exception { - MockitoAnnotations.openMocks(this) - .close(); - provider = new OAuthTokenProvider(oAuthClient); - } - - @Test - void testGetTokenIsLazyAndDoesNotInvokeOAuthClientUntilSubscribed() { - Mockito.when(oAuthClient.getToken()) - .thenReturn(token); - Mockito.when(token.getAuthorizationHeaderValue()) - .thenReturn("Bearer abc123"); - - Mono mono = provider.getToken(connectionContext); - - Mockito.verify(oAuthClient, Mockito.never()) - .getToken(); - - String result = mono.block(); - - Assertions.assertEquals("Bearer abc123", result); - Mockito.verify(oAuthClient) - .getToken(); - } - - @Test - void testGetTokenInvokesOAuthClientOncePerSubscription() { - Mockito.when(oAuthClient.getToken()) - .thenReturn(token); - Mockito.when(token.getAuthorizationHeaderValue()) - .thenReturn("Bearer abc123"); - - Mono mono = provider.getToken(connectionContext); - - mono.block(); - mono.block(); - - Mockito.verify(oAuthClient, Mockito.times(2)) - .getToken(); - } - - @Test - void testGetTokenPropagatesOAuthClientFailure() { - RuntimeException failure = new RuntimeException("token retrieval failed"); - Mockito.when(oAuthClient.getToken()) - .thenThrow(failure); - - Mono mono = provider.getToken(connectionContext); - - RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, mono::block); - Assertions.assertSame(failure, thrown); - } -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudApplicationTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudApplicationTest.java deleted file mode 100644 index 514fe959c6..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudApplicationTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.List; -import java.util.Map; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudSpace; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableLifecycle; -import org.cloudfoundry.client.v3.BuildpackData; -import org.cloudfoundry.client.v3.DockerData; -import org.cloudfoundry.client.v3.Lifecycle; -import org.cloudfoundry.client.v3.LifecycleType; -import org.cloudfoundry.client.v3.applications.ApplicationResource; -import org.cloudfoundry.client.v3.applications.ApplicationState; -import org.junit.jupiter.api.Test; - -class RawCloudApplicationTest { - - private static final String STATE = "STARTED"; - private static final String BUILDPACK = "ruby_buildpack"; - private static final String STACK_NAME = "cflinuxfs3"; - private static final String SPACE_NAME = "test"; - private static final CloudSpace SPACE = ImmutableCloudSpace.builder() - .name(SPACE_NAME) - .build(); - private static final String EXPECTED_BUILDPACK = "ruby_buildpack"; - private static final String BUILDPACK_URL = "custom-buildpack-url"; - - private static final String EXPECTED_STACK = "cflinuxfs3"; - private static final CloudApplication.State EXPECTED_STATE = CloudApplication.State.STARTED; - - @Test - void testDeriveForBuildpackApp() { - RawCloudEntityTest.testDerive(buildApplication(buildBuildpackLifecycle()), buildRawApplication(buildBuildpackLifecycleResource())); - } - - @Test - void testDeriveForDockerApp() { - RawCloudEntityTest.testDerive(buildApplication(buildDockerLifecycle()), buildRawApplication(buildDockerLifecycleResource())); - } - - @Test - void testDeriveForCnbApp() { - RawCloudEntityTest.testDerive(buildApplication(buildCnbLifecycle()), buildRawApplication(buildCnbLifecycleResource())); - } - - private static CloudApplication buildApplication(org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle lifecycle) { - return ImmutableCloudApplication.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) - .v3Metadata(RawCloudEntityTest.V3_METADATA) - .name(RawCloudEntityTest.NAME) - .state(EXPECTED_STATE) - .lifecycle(lifecycle) - .space(SPACE) - .build(); - } - - private static org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle buildBuildpackLifecycle() { - return ImmutableLifecycle.builder() - .type(org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType.BUILDPACK) - .data(buildLifecycleData(EXPECTED_BUILDPACK)) - .build(); - } - - private org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle buildCnbLifecycle() { - return ImmutableLifecycle.builder() - .type(org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType.CNB) - .data(buildLifecycleData(BUILDPACK_URL)) - .build(); - } - - private static org.cloudfoundry.multiapps.controller.client.facade.domain.Lifecycle buildDockerLifecycle() { - return ImmutableLifecycle.builder() - .type(org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType.DOCKER) - .data(Map.of()) - .build(); - } - - private static Map buildLifecycleData(String buildpack) { - return Map.of("buildpacks", List.of(buildpack), "stack", EXPECTED_STACK); - } - - private static RawCloudApplication buildRawApplication(Lifecycle lifecycle) { - return ImmutableRawCloudApplication.builder() - .application(buildApplicationResource(lifecycle)) - .space(SPACE) - .build(); - } - - private static ApplicationResource buildApplicationResource(Lifecycle lifecycle) { - return ApplicationResource.builder() - .metadata(RawCloudEntityTest.V3_METADATA) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .state(ApplicationState.valueOf(STATE)) - .id(RawCloudEntityTest.GUID.toString()) - .lifecycle(lifecycle) - .name("foo") - .build(); - } - - private static Lifecycle buildBuildpackLifecycleResource() { - return Lifecycle.builder() - .type(LifecycleType.BUILDPACK) - .data(BuildpackData.builder() - .buildpack(BUILDPACK) - .stack(STACK_NAME) - .build()) - .build(); - } - - private Lifecycle buildCnbLifecycleResource() { - return Lifecycle.builder() - .type(LifecycleType.CNB) - .data(BuildpackData.builder() - .buildpack(BUILDPACK_URL) - .stack(STACK_NAME) - .build()) - .build(); - } - - private static Lifecycle buildDockerLifecycleResource() { - return Lifecycle.builder() - .type(LifecycleType.DOCKER) - .data(DockerData.builder() - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudAsyncJobTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudAsyncJobTest.java deleted file mode 100644 index 1340e590ff..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudAsyncJobTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.List; - -import org.cloudfoundry.client.v3.Error; -import org.cloudfoundry.client.v3.jobs.GetJobResponse; -import org.cloudfoundry.client.v3.jobs.Job; -import org.cloudfoundry.client.v3.jobs.JobState; -import org.cloudfoundry.client.v3.jobs.Warning; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; - -class RawCloudAsyncJobTest { - - private static final String OPERATION = "service_broker.delete"; - private static final String WARNING_1 = "warning1"; - private static final String WARNING_2 = "warning2"; - private static final String WARNINGS = "warning1,warning2"; - private static final int ERROR_CODE_1 = 10008; - private static final int ERROR_CODE_2 = 1000; - private static final String ERROR_TITLE_1 = "CF-UnprocessableEntity"; - private static final String ERROR_TITLE_2 = "CF-InvalidAuthToken"; - private static final String ERROR_DETAIL_1 = "something went wrong"; - private static final String ERROR_DETAIL_2 = "not valid token"; - private static final String ERRORS = "10008 CF-UnprocessableEntity something went wrong,1000 CF-InvalidAuthToken not valid token"; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedTask(), buildRawTask()); - } - - private CloudAsyncJob buildExpectedTask() { - return ImmutableCloudAsyncJob.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .state(JobState.FAILED) - .operation(OPERATION) - .warnings(WARNINGS) - .errors(ERRORS) - .build(); - } - - private RawCloudAsyncJob buildRawTask() { - return ImmutableRawCloudAsyncJob.of(buildTestResource()); - } - - private Job buildTestResource() { - return GetJobResponse.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .state(JobState.FAILED) - .operation(OPERATION) - .warnings(buildWarnings()) - .errors(buildErrors()) - .build(); - } - - private List buildWarnings() { - return List.of(Warning.builder() - .detail(WARNING_1) - .build(), - Warning.builder() - .detail(WARNING_2) - .build()); - } - - private List buildErrors() { - return List.of(Error.builder() - .code(ERROR_CODE_1) - .title(ERROR_TITLE_1) - .detail(ERROR_DETAIL_1) - .build(), - Error.builder() - .code(ERROR_CODE_2) - .title(ERROR_TITLE_2) - .detail(ERROR_DETAIL_2) - .build()); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudBuildTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudBuildTest.java deleted file mode 100644 index c35ec79de0..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudBuildTest.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.UUID; - -import org.cloudfoundry.client.v3.BuildpackData; -import org.cloudfoundry.client.v3.Lifecycle; -import org.cloudfoundry.client.v3.LifecycleType; -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.builds.Build; -import org.cloudfoundry.client.v3.builds.BuildResource; -import org.cloudfoundry.client.v3.builds.BuildState; -import org.cloudfoundry.client.v3.builds.CreatedBy; -import org.cloudfoundry.client.v3.builds.Droplet; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDropletInfo; - -class RawCloudBuildTest { - - private static final String PACKAGE_GUID_STRING = "d0bd1437-f112-48cd-b777-6b160cf2fff0"; - private static final String DROPLET_GUID_STRING = "47e172c9-bfb2-41df-bf3f-850108c241ef"; - private static final Droplet DROPLET = buildTestDroplet(); - private static final String CREATED_BY_GUID_STRING = "37964fd8-a234-4c04-af31-c3fe5e477bed"; - private static final String CREATED_BY_NAME = "admin"; - private static final LifecycleType LIFECYCLE_TYPE = LifecycleType.BUILDPACK; - private static final BuildState STATE = BuildState.FAILED; - private static final String ERROR = "blabla"; - - private static final UUID PACKAGE_GUID = UUID.fromString(PACKAGE_GUID_STRING); - private static final UUID DROPLET_GUID = UUID.fromString(DROPLET_GUID_STRING); - private static final UUID CREATED_BY_GUID = UUID.fromString(CREATED_BY_GUID_STRING); - - private static final CloudBuild.State EXPECTED_STATE = CloudBuild.State.FAILED; - private static final DropletInfo EXPECTED_DROPLET_INFO = buildExpectedDropletInfo(); - - private static CloudBuild buildExpectedBuild() { - return buildExpectedBuild(null); - } - - private static CloudBuild buildExpectedBuild(DropletInfo dropletInfo) { - return ImmutableCloudBuild.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .createdBy(buildExpectedCreatedBy()) - .dropletInfo(dropletInfo) - .packageInfo(buildExpectedPackageInfo()) - .state(EXPECTED_STATE) - .error(ERROR) - .build(); - } - - private static CloudBuild.CreatedBy buildExpectedCreatedBy() { - return ImmutableCloudBuild.ImmutableCreatedBy.builder() - .guid(CREATED_BY_GUID) - .name(CREATED_BY_NAME) - .build(); - } - - private static DropletInfo buildExpectedDropletInfo() { - return ImmutableDropletInfo.builder() - .guid(DROPLET_GUID) - .build(); - } - - private static CloudBuild.PackageInfo buildExpectedPackageInfo() { - return ImmutableCloudBuild.ImmutablePackageInfo.builder() - .guid(PACKAGE_GUID) - .build(); - } - - private static Build buildTestResource(Droplet droplet) { - return BuildResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .inputPackage(buildTestPackage()) - .createdBy(buildTestCreatedBy()) - .lifecycle(buildTestLifecycle()) - .droplet(droplet) - .state(STATE) - .error(ERROR) - .build(); - } - - private static Relationship buildTestPackage() { - return Relationship.builder() - .id(PACKAGE_GUID_STRING) - .build(); - } - - private static Droplet buildTestDroplet() { - return Droplet.builder() - .id(DROPLET_GUID_STRING) - .build(); - } - - private static CreatedBy buildTestCreatedBy() { - return CreatedBy.builder() - .id(CREATED_BY_GUID_STRING) - .name(CREATED_BY_NAME) - .email(CREATED_BY_NAME) - .build(); - } - - private static Lifecycle buildTestLifecycle() { - return Lifecycle.builder() - .data(buildTestLifecycleData()) - .type(LIFECYCLE_TYPE) - .build(); - } - - private static BuildpackData buildTestLifecycleData() { - return BuildpackData.builder() - .build(); - } - - @Test - void testDeriveWithoutDroplet() { - RawCloudEntityTest.testDerive(buildExpectedBuild(), buildRawBuild()); - } - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedBuild(EXPECTED_DROPLET_INFO), buildRawBuild(DROPLET)); - } - - private RawCloudBuild buildRawBuild() { - return buildRawBuild(null); - } - - private RawCloudBuild buildRawBuild(Droplet droplet) { - return ImmutableRawCloudBuild.of(buildTestResource(droplet)); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudDomainTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudDomainTest.java deleted file mode 100644 index 65fd5a6642..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudDomainTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.ToManyRelationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.domains.Domain; -import org.cloudfoundry.client.v3.domains.DomainRelationships; -import org.cloudfoundry.client.v3.organizations.GetOrganizationDefaultDomainResponse; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudDomain; - -class RawCloudDomainTest { - - private static final String DOMAIN_NAME = "example.com"; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedDomain(), buildRawDomain()); - } - - private static CloudDomain buildExpectedDomain() { - return ImmutableCloudDomain.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .name(DOMAIN_NAME) - .build(); - } - - private static RawCloudDomain buildRawDomain() { - return ImmutableRawCloudDomain.of(buildTestResource()); - } - - private static Domain buildTestResource() { - return GetOrganizationDefaultDomainResponse.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .name(DOMAIN_NAME) - .isInternal(false) - .relationships(DomainRelationships.builder() - .organization(ToOneRelationship.builder() - .build()) - .sharedOrganizations(ToManyRelationship.builder() - .build()) - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEntityTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEntityTest.java deleted file mode 100644 index 51b1fb5007..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEntityTest.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.applications.ApplicationState; -import org.cloudfoundry.client.v3.organizations.Organization; -import org.cloudfoundry.client.v3.organizations.OrganizationResource; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudBuild; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Derivable; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; - -class RawCloudEntityTest { - - static final String GUID_STRING = "3725650a-8725-4401-a949-c68f83d54a86"; - static final String CREATED_AT_STRING = "2017-06-22T13:38:41Z"; - static final String UPDATED_AT_STRING = "2019-03-21T12:29:24Z"; - - static final String NAME = "foo"; - static final UUID GUID = UUID.fromString(GUID_STRING); - static final LocalDateTime CREATED_AT = fromZonedDateTime(ZonedDateTime.of(2017, 6, 22, 13, 38, 41, 0, ZoneId.of("Z"))); - static final LocalDateTime UPDATED_AT = fromZonedDateTime(ZonedDateTime.of(2019, 3, 21, 12, 29, 24, 0, ZoneId.of("Z"))); - static final Map V3_ANNOTATIONS = Map.of("annotation1", "value1", "annotation2", "value2"); - static final Map V3_LABELS = Map.of("label1", "value1", "label2", "value2"); - - static final Metadata V3_METADATA = Metadata.builder() - .annotations(V3_ANNOTATIONS) - .labels(V3_LABELS) - .build(); - - static final CloudMetadata EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE = ImmutableCloudMetadata.builder() - .guid(GUID) - .createdAt(CREATED_AT) - .updatedAt(UPDATED_AT) - .build(); - static final CloudMetadata EXPECTED_METADATA_V3 = ImmutableCloudMetadata.builder() - .guid(GUID) - .createdAt(CREATED_AT) - .updatedAt(UPDATED_AT) - .build(); - - static LocalDateTime fromZonedDateTime(ZonedDateTime dateTime) { - return dateTime.toLocalDateTime(); - } - - @Test - void testParseV3ResourceMetadata() { - Organization resource = OrganizationResource.builder() - .id(GUID_STRING) - .createdAt(CREATED_AT_STRING) - .updatedAt(UPDATED_AT_STRING) - .name(NAME) - .metadata(V3_METADATA) - .build(); - - CloudMetadata metadata = RawCloudEntity.parseResourceMetadata(resource); - assertEquals(EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE, metadata); - } - - @Test - void testParseNullableGuid() { - assertNull(RawCloudEntity.parseNullableGuid(null)); - } - - @Test - void testParseNullableDate() { - assertNull(RawCloudEntity.parseNullableDate(null)); - } - - @Test - void testParseGuid() { - assertEquals(GUID, RawCloudEntity.parseGuid(GUID_STRING)); - } - - @Test - void testParseGuidWithInvalidGuid() { - assertNull(RawCloudEntity.parseGuid("foo")); - } - - @Test - void testParseDateWithInvalidDate() { - assertNull(RawCloudEntity.parseDate("foo")); - } - - @Test - void testParseDateWithInvalidFormat() { - assertNull(RawCloudEntity.parseDate("16.07.2019 15:30:25")); - } - - @Test - void testParseDate() { - assertEquals(CREATED_AT, RawCloudEntity.parseDate(CREATED_AT_STRING)); - } - - @Test - void testParseEnum() { - CloudApplication.State state = RawCloudEntity.parseEnum(ApplicationState.STARTED, CloudApplication.State.class); - assertEquals(CloudApplication.State.STARTED, state); - } - - @Test - void testParseEnumWithIncompatibleEnumTypes() { - Assertions.assertThrows(IllegalArgumentException.class, - () -> RawCloudEntity.parseEnum(ApplicationState.STARTED, CloudBuild.State.class)); - } - - @Test - void testDeriveFromNullable() { - assertEquals(NAME, RawCloudEntity.deriveFromNullable(() -> NAME)); - } - - @Test - void testDeriveFromNullableWithNull() { - assertNull(RawCloudEntity.deriveFromNullable(null)); - } - - @Test - void testDerive() { - List> derivables = Arrays.asList(() -> NAME, () -> GUID_STRING); - assertEquals(Arrays.asList(NAME, GUID_STRING), RawCloudEntity.derive(derivables)); - } - - static void testDerive(T expected, Derivable derivable) { - assertEquals(expected, derivable.derive()); - } - - static ToOneRelationship buildToOneRelationship(String id) { - return ToOneRelationship.builder() - .data(Relationship.builder() - .id(id) - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEventTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEventTest.java deleted file mode 100644 index 2a826d312a..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudEventTest.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.UUID; - -import org.cloudfoundry.client.v3.auditevents.AuditEventActor; -import org.cloudfoundry.client.v3.auditevents.AuditEventResource; -import org.cloudfoundry.client.v3.auditevents.AuditEventTarget; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEvent.Participant; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudEvent; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudEvent.ImmutableParticipant; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; - -class RawCloudEventTest { - - private static final String TARGET_GUID_STRING = "b7c57058-afc0-43c9-afee-64019e850bef"; - private static final String TARGET_NAME = "foo"; - private static final String TARGET_TYPE = "app"; - private static final String ACTOR_GUID_STRING = "72c1e48a-9629-4cfe-a64c-f53851d81f61"; - private static final String ACTOR_NAME = "john"; - private static final String ACTOR_TYPE = "user"; - private static final String TIMESTAMP_STRING = "2019-07-03T20:00:46Z"; - private static final String TYPE = "audit.app.create"; - - private static final UUID TARGET_GUID = UUID.fromString(TARGET_GUID_STRING); - private static final UUID ACTOR_GUID = UUID.fromString(ACTOR_GUID_STRING); - private static final LocalDateTime TIMESTAMP = RawCloudEntityTest.fromZonedDateTime(ZonedDateTime.of(2019, 7, 3, 20, 0, 46, 0, - ZoneId.of("Z"))); - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedEvent(), buildRawEvent()); - } - - @Test - void testDeriveWithEmptyResponse() { - RawCloudEntityTest.testDerive(buildEmptyExpectedEvent(), buildEmptyRawEvent()); - } - - private CloudEvent buildExpectedEvent() { - return ImmutableCloudEvent.builder() - .metadata(ImmutableCloudMetadata.builder() - .guid(RawCloudEntityTest.GUID) - .createdAt(TIMESTAMP) - .updatedAt(RawCloudEntityTest.UPDATED_AT) - .build()) - .target(buildExpectedTarget()) - .actor(buildExpectedActor()) - .type(TYPE) - .build(); - } - - private Participant buildExpectedTarget() { - return ImmutableParticipant.builder() - .guid(TARGET_GUID) - .name(TARGET_NAME) - .type(TARGET_TYPE) - .build(); - } - - private Participant buildExpectedActor() { - return ImmutableParticipant.builder() - .guid(ACTOR_GUID) - .name(ACTOR_NAME) - .type(ACTOR_TYPE) - .build(); - } - - private CloudEvent buildEmptyExpectedEvent() { - return ImmutableCloudEvent.builder() - .metadata(ImmutableCloudMetadata.builder() - .guid(RawCloudEntityTest.GUID) - .createdAt(TIMESTAMP) - .updatedAt(RawCloudEntityTest.UPDATED_AT) - .build()) - .target(buildEmptyParticipant()) - .actor(buildEmptyParticipant()) - .build(); - } - - private Participant buildEmptyParticipant() { - return ImmutableParticipant.builder() - .build(); - } - - private RawCloudEvent buildRawEvent() { - return ImmutableRawCloudEvent.of(buildTestResource()); - } - - private AuditEventResource buildTestResource() { - return AuditEventResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(TIMESTAMP_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .type(TYPE) - .auditEventActor(AuditEventActor.builder() - .id(ACTOR_GUID_STRING) - .name(ACTOR_NAME) - .type(ACTOR_TYPE) - .build()) - .auditEventTarget(AuditEventTarget.builder() - .id(TARGET_GUID_STRING) - .type(TARGET_TYPE) - .name(TARGET_NAME) - .build()) - .build(); - } - - private RawCloudEvent buildEmptyRawEvent() { - return ImmutableRawCloudEvent.of(buildEmptyTestResource()); - } - - private AuditEventResource buildEmptyTestResource() { - return AuditEventResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(TIMESTAMP_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudPackageTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudPackageTest.java deleted file mode 100644 index 2f636ea79b..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudPackageTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.Checksum; -import org.cloudfoundry.client.v3.ChecksumType; -import org.cloudfoundry.client.v3.packages.BitsData; -import org.cloudfoundry.client.v3.packages.DockerData; -import org.cloudfoundry.client.v3.packages.Package; -import org.cloudfoundry.client.v3.packages.PackageData; -import org.cloudfoundry.client.v3.packages.PackageResource; -import org.cloudfoundry.client.v3.packages.PackageState; -import org.cloudfoundry.client.v3.packages.PackageType; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudPackage; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableBitsData; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudPackage; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableDockerData; -import org.cloudfoundry.multiapps.controller.client.facade.domain.Status; - -class RawCloudPackageTest { - - private static final PackageState STATE = PackageState.EXPIRED; - private static final String ERROR = "blabla"; - private static final String DOCKER_IMAGE = "cloudfoundry/test"; - private static final ChecksumType CHECKSUM_TYPE = ChecksumType.SHA256; - private static final String CHECKSUM_VALUE = "7251a608605c0f45710f8415bba0d117c90598824697a2a5d00850ea9b179112"; - - private static final Status EXPECTED_STATUS = Status.EXPIRED; - private static final String EXPECTED_CHECKSUM_ALGORITHM = CHECKSUM_TYPE.toString(); - - @Test - void testDeriveWithBitsData() { - RawCloudEntityTest.testDerive(buildExpectedPackage(CloudPackage.Type.BITS), buildRawPackage(PackageType.BITS)); - } - - @Test - void testDeriveWithDockerData() { - RawCloudEntityTest.testDerive(buildExpectedPackage(CloudPackage.Type.DOCKER), buildRawPackage(PackageType.DOCKER)); - } - - private static CloudPackage buildExpectedPackage(CloudPackage.Type type) { - return ImmutableCloudPackage.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .data(buildExpectedData(type)) - .type(type) - .status(EXPECTED_STATUS) - .build(); - } - - private static CloudPackage.PackageData buildExpectedData(CloudPackage.Type type) { - if (type == CloudPackage.Type.DOCKER) { - return ImmutableDockerData.builder() - .image(DOCKER_IMAGE) - .build(); - } - return ImmutableBitsData.builder() - .checksum(buildExpectedChecksum()) - .error(ERROR) - .build(); - } - - private static org.cloudfoundry.multiapps.controller.client.facade.domain.BitsData.Checksum buildExpectedChecksum() { - return ImmutableBitsData.ImmutableChecksum.builder() - .algorithm(EXPECTED_CHECKSUM_ALGORITHM) - .value(CHECKSUM_VALUE) - .build(); - } - - private static RawCloudPackage buildRawPackage(PackageType type) { - return ImmutableRawCloudPackage.builder() - .resource(buildTestResource(type)) - .build(); - } - - private static Package buildTestResource(PackageType type) { - return PackageResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .type(type) - .data(buildTestData(type)) - .state(STATE) - .build(); - } - - private static PackageData buildTestData(PackageType type) { - if (type == PackageType.DOCKER) { - return buildDockerData(); - } - return buildBitsData(); - } - - private static DockerData buildDockerData() { - return DockerData.builder() - .image(DOCKER_IMAGE) - .build(); - } - - private static BitsData buildBitsData() { - return BitsData.builder() - .checksum(buildTestChecksum()) - .error(ERROR) - .build(); - } - - private static Checksum buildTestChecksum() { - return Checksum.builder() - .type(CHECKSUM_TYPE) - .value(CHECKSUM_VALUE) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java deleted file mode 100644 index b57652433c..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.processes.Data; -import org.cloudfoundry.client.v3.processes.HealthCheck; -import org.cloudfoundry.client.v3.processes.HealthCheckType; -import org.cloudfoundry.client.v3.processes.ProcessRelationships; -import org.cloudfoundry.client.v3.processes.ProcessResource; -import org.cloudfoundry.client.v3.processes.ReadinessHealthCheck; -import org.cloudfoundry.client.v3.processes.ReadinessHealthCheckType; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudProcess; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -class RawCloudProcessTest { - - private static final String COMMAND = "bundle exec rackup"; - private static final Integer DISK_IN_MB = 1024; - private static final Integer INSTANCES = 2; - private static final Integer MEMORY_IN_MB = 512; - private static final String HTTP_ENDPOINT = "/health"; - private static final Integer TIMEOUT = 30; - private static final Integer INVOCATION_TIMEOUT = 5; - private static final Integer INTERVAL = 10; - private static final String READINESS_HTTP_ENDPOINT = "/ready"; - private static final Integer READINESS_INVOCATION_TIMEOUT = 4; - private static final Integer READINESS_INTERVAL = 8; - - @Test - void testDeriveWithFullHealthCheckDataIncludingInterval() { - RawCloudProcess raw = ImmutableRawCloudProcess.of(buildProcessResource(HealthCheckType.HTTP, buildHealthCheckData(), - ReadinessHealthCheckType.HTTP, - buildReadinessHealthCheckData())); - - CloudProcess derived = raw.derive(); - - assertEquals(buildExpected(org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType.HTTP, INTERVAL, - ReadinessHealthCheckType.HTTP.getValue()), - derived); - } - - @Test - void testDeriveWithMissingHealthCheckDataLeavesIntervalNull() { - // healthCheck.getData() returns null -> all four health-check Integer/String fields stay null - RawCloudProcess raw = ImmutableRawCloudProcess.of(buildProcessResource(HealthCheckType.PORT, null, - ReadinessHealthCheckType.PORT, null)); - - CloudProcess derived = raw.derive(); - - assertEquals(org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType.PORT, derived.getHealthCheckType()); - assertNull(derived.getHealthCheckTimeout()); - assertNull(derived.getHealthCheckInvocationTimeout()); - assertNull(derived.getHealthCheckHttpEndpoint()); - assertNull(derived.getHealthCheckInterval()); - assertNull(derived.getReadinessHealthCheckInvocationTimeout()); - assertNull(derived.getReadinessHealthCheckHttpEndpoint()); - assertNull(derived.getReadinessHealthCheckInterval()); - } - - @Test - void testDeriveWithHealthCheckDataPresentButIntervalNull() { - Data dataWithoutInterval = Data.builder() - .endpoint(HTTP_ENDPOINT) - .timeout(TIMEOUT) - .invocationTimeout(INVOCATION_TIMEOUT) - .build(); - RawCloudProcess raw = ImmutableRawCloudProcess.of(buildProcessResource(HealthCheckType.HTTP, dataWithoutInterval, - ReadinessHealthCheckType.PORT, null)); - - CloudProcess derived = raw.derive(); - - assertEquals(TIMEOUT, derived.getHealthCheckTimeout()); - assertEquals(INVOCATION_TIMEOUT, derived.getHealthCheckInvocationTimeout()); - assertEquals(HTTP_ENDPOINT, derived.getHealthCheckHttpEndpoint()); - assertNull(derived.getHealthCheckInterval()); - } - - private static ProcessResource buildProcessResource(HealthCheckType healthCheckType, Data healthCheckData, - ReadinessHealthCheckType readinessHealthCheckType, Data readinessHealthCheckData) { - HealthCheck.Builder healthCheckBuilder = HealthCheck.builder() - .type(healthCheckType); - if (healthCheckData != null) { - healthCheckBuilder.data(healthCheckData); - } - ReadinessHealthCheck.Builder readinessBuilder = ReadinessHealthCheck.builder() - .type(readinessHealthCheckType); - if (readinessHealthCheckData != null) { - readinessBuilder.data(readinessHealthCheckData); - } - return ProcessResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .command(COMMAND) - .diskInMb(DISK_IN_MB) - .instances(INSTANCES) - .memoryInMb(MEMORY_IN_MB) - .type("web") - .metadata(Metadata.builder() - .build()) - .relationships(ProcessRelationships.builder() - .build()) - .healthCheck(healthCheckBuilder.build()) - .readinessHealthCheck(readinessBuilder.build()) - .build(); - } - - private static Data buildHealthCheckData() { - return Data.builder() - .endpoint(HTTP_ENDPOINT) - .timeout(TIMEOUT) - .invocationTimeout(INVOCATION_TIMEOUT) - .interval(INTERVAL) - .build(); - } - - private static Data buildReadinessHealthCheckData() { - return Data.builder() - .endpoint(READINESS_HTTP_ENDPOINT) - .invocationTimeout(READINESS_INVOCATION_TIMEOUT) - .interval(READINESS_INTERVAL) - .build(); - } - - private static CloudProcess buildExpected(org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType type, - Integer interval, String readinessHealthCheckType) { - return ImmutableCloudProcess.builder() - .command(COMMAND) - .instances(INSTANCES) - .memoryInMb(MEMORY_IN_MB) - .diskInMb(DISK_IN_MB) - .healthCheckType(type) - .healthCheckHttpEndpoint(HTTP_ENDPOINT) - .healthCheckTimeout(TIMEOUT) - .healthCheckInvocationTimeout(INVOCATION_TIMEOUT) - .healthCheckInterval(interval) - .readinessHealthCheckType(readinessHealthCheckType) - .readinessHealthCheckHttpEndpoint(READINESS_HTTP_ENDPOINT) - .readinessHealthCheckInvocationTimeout(READINESS_INVOCATION_TIMEOUT) - .readinessHealthCheckInterval(READINESS_INTERVAL) - .build(); - } -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudRouteTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudRouteTest.java deleted file mode 100644 index 94f19d18d3..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudRouteTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.List; -import java.util.UUID; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.routes.Application; -import org.cloudfoundry.client.v3.routes.Destination; -import org.cloudfoundry.client.v3.routes.Route; -import org.cloudfoundry.client.v3.routes.RouteRelationships; -import org.cloudfoundry.client.v3.routes.RouteResource; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudDomain; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudRoute; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudDomain; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudRoute; - -class RawCloudRouteTest { - - private static final String HOST = "foo"; - private static final String DOMAIN_NAME = "example.com"; - private static final CloudDomain DOMAIN = buildTestDomain(); - private static final Integer APPS_USING_ROUTE = 2; - private static final UUID DESTINATION_1_GUID = UUID.randomUUID(); - private static final UUID APPLICATION_1_GUID = UUID.randomUUID(); - private static final String DESTINATION_1_PROTOCOL = "http2"; - private static final UUID DESTINATION_2_GUID = UUID.randomUUID(); - private static final UUID APPLICATION_2_GUID = UUID.randomUUID(); - private static final String DESTINATION_2_PROTOCOL = "http1"; - private static final List DESTINATIONS = buildTestDestinations(); - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedRoute(), buildRawRoute()); - } - - private static CloudRoute buildExpectedRoute() { - return ImmutableCloudRoute.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) - .host(HOST) - .domain(DOMAIN) - .path("") - .appsUsingRoute(APPS_USING_ROUTE) - .url(HOST + "." + DOMAIN_NAME) - .build(); - } - - private static RawCloudRoute buildRawRoute() { - return ImmutableRawCloudRoute.builder() - .route(buildTestRoute()) - .build(); - } - - private static Route buildTestRoute() { - return RouteResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .relationships(RouteRelationships.builder() - .space(buildToOneRelationship(RawCloudEntityTest.GUID)) - .domain(buildToOneRelationship(RawCloudEntityTest.GUID)) - .build()) - .metadata(RawCloudEntityTest.V3_METADATA) - .host(HOST) - .path("") - .url(HOST + "." + DOMAIN_NAME) - .addAllDestinations(DESTINATIONS) - .build(); - } - - private static CloudDomain buildTestDomain() { - return ImmutableCloudDomain.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) - .name(DOMAIN_NAME) - .build(); - } - - private static List buildTestDestinations() { - return List.of(buildDestination(DESTINATION_1_GUID.toString(), APPLICATION_1_GUID.toString(), DESTINATION_1_PROTOCOL), - buildDestination(DESTINATION_2_GUID.toString(), APPLICATION_2_GUID.toString(), DESTINATION_2_PROTOCOL)); - } - - private static Destination buildDestination(String destinationGuid, String applicationGuid, String protocol) { - return Destination.builder() - .destinationId(destinationGuid) - .application(Application.builder() - .applicationId(applicationGuid) - .build()) - .protocol(protocol) - .build(); - } - - private static ToOneRelationship buildToOneRelationship(UUID guid) { - return ToOneRelationship.builder() - .data(buildRelationship(guid)) - .build(); - } - - private static Relationship buildRelationship(UUID guid) { - return Relationship.builder() - .id(guid.toString()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBindingTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBindingTest.java deleted file mode 100644 index 41d468060a..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBindingTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.time.LocalDateTime; -import java.util.UUID; - -import org.cloudfoundry.client.v3.LastOperation; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingRelationships; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingType; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBinding; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; - -class RawCloudServiceBindingTest { - - private static final String APPLICATION_GUID_STRING = "3725650a-8725-4401-a949-c68f83d54a86"; - private static final String SERVICE_INSTANCE_GUID_STRING = "3725650a-8725-4401-a949-c68f83d54a86"; - private static final UUID APPLICATION_GUID = UUID.fromString(APPLICATION_GUID_STRING); - private static final UUID SERVICE_INSTANCE_GUID = UUID.fromString(SERVICE_INSTANCE_GUID_STRING); - private static final LastOperation LAST_OPERATION = buildLastOperation(); - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedServiceBinding(), buildRawServiceBinding()); - } - - private static CloudServiceBinding buildExpectedServiceBinding() { - return ImmutableCloudServiceBinding.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) - .applicationGuid(APPLICATION_GUID) - .serviceInstanceGuid(SERVICE_INSTANCE_GUID) - .serviceBindingOperation(ServiceCredentialBindingOperation.from(LAST_OPERATION)) - .build(); - } - - private static RawCloudServiceBinding buildRawServiceBinding() { - return ImmutableRawCloudServiceBinding.builder() - .serviceBinding(buildTestResource()) - - .build(); - } - - private static ServiceBindingResource buildTestResource() { - return ServiceBindingResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .type(ServiceBindingType.APPLICATION) - .lastOperation(LAST_OPERATION) - .relationships(ServiceBindingRelationships.builder() - .serviceInstance(RawCloudEntityTest.buildToOneRelationship(SERVICE_INSTANCE_GUID_STRING)) - .application(RawCloudEntityTest.buildToOneRelationship(APPLICATION_GUID_STRING)) - .build()) - .build(); - } - - private static LastOperation buildLastOperation() { - return LastOperation.builder() - .state(ServiceCredentialBindingOperation.State.SUCCEEDED.toString()) - .type(ServiceCredentialBindingOperation.Type.CREATE.toString()) - .createdAt(LocalDateTime.now() - .toString()) - .updatedAt(LocalDateTime.now() - .toString()) - .description("Service binding created") - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBrokerTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBrokerTest.java deleted file mode 100644 index c3c9dca252..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceBrokerTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerRelationships; -import org.cloudfoundry.client.v3.servicebrokers.ServiceBrokerResource; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBroker; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBroker; - -class RawCloudServiceBrokerTest { - - private static final String NAME = "auditlog-broker"; - private static final String URL = "https://auditlog-broker.example.com"; - private static final String SPACE_GUID_STRING = "ef93547f-74c3-4bad-ba69-b7dc4f212622"; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedServiceBroker(), buildRawServiceBroker()); - } - - private static CloudServiceBroker buildExpectedServiceBroker() { - return ImmutableCloudServiceBroker.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .name(NAME) - .spaceGuid(SPACE_GUID_STRING) - .url(URL) - .build(); - } - - private static RawCloudServiceBroker buildRawServiceBroker() { - return ImmutableRawCloudServiceBroker.builder() - .serviceBroker(buildTestServiceBroker()) - .build(); - } - - private static ServiceBrokerResource buildTestServiceBroker() { - return ServiceBrokerResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .name(NAME) - .url(URL) - .relationships(ServiceBrokerRelationships.builder() - .space(ToOneRelationship.builder() - .data(Relationship.builder() - .id(SPACE_GUID_STRING) - .build()) - .build()) - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceInstanceTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceInstanceTest.java deleted file mode 100644 index 9645cca49a..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceInstanceTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.cloudfoundry.client.v3.LastOperation; -import org.cloudfoundry.client.v3.MaintenanceInfo; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceRelationships; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceResource; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; - -class RawCloudServiceInstanceTest { - - private static final String SERVICE_INSTANCE_ID = "7cb26016-342d-11ed-a261-0242ac120002"; - private static final String SERVICE_INSTANCE_NAME = "v3-service-instance"; - private static final String DATE = "2022-01-01T13:35:11Z"; - private static final String DASHBOARD_URL = "dashboard-url"; - private static final String LAST_OPERATION_DESCRIPTION = "Last operation has succeeded"; - private static final String LAST_OPERATION_STATE = "succeeded"; - private static final String LAST_OPERATION_TYPE = "create"; - private static final String MAINTENANCE_INFO_DESCRIPTION = "A long time ago"; - private static final String MAINTENANCE_INFO_VERSION = "0.9.9"; - private static final Map METADATA_ANNOTATIONS = Map.of("foo", "bar"); - private static final Map METADATA_LABELS = Map.of("baz", "mop"); - private static final String SPACE_GUID = "5f5a8db4-342c-11ed-a261-0242ac120002"; - private static final String SERVICE_PLAN_GUID = "810a0322-342c-11ed-a261-0242ac120002"; - private static final String ROUTE_SERVICE_URL = "https://route-service-url"; - private static final String SYSLOG_DRAIN_URL = "https://syslog-drain"; - private static final List TAGS = List.of("one", "two", "three"); - private static final ServiceInstanceType SERVICE_INSTANCE_TYPE = ServiceInstanceType.MANAGED; - private static final boolean UPDATE_AVAILABLE = false; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedServiceInstance(), buildRawServiceInstance()); - } - - private CloudServiceInstance buildExpectedServiceInstance() { - return ImmutableCloudServiceInstance.builder() - .metadata(ImmutableCloudMetadata.builder() - .createdAt(ZonedDateTime.parse(DATE, - DateTimeFormatter.ISO_DATE_TIME) - .toLocalDateTime()) - .updatedAt(ZonedDateTime.parse(DATE, - DateTimeFormatter.ISO_DATE_TIME) - .toLocalDateTime()) - .guid(UUID.fromString(SERVICE_INSTANCE_ID)) - .build()) - .name(SERVICE_INSTANCE_NAME) - .v3Metadata(Metadata.builder() - .annotations(METADATA_ANNOTATIONS) - .labels(METADATA_LABELS) - .build()) - .tags(TAGS) - .build(); - } - - private RawV3CloudServiceInstance buildRawServiceInstance() { - return ImmutableRawV3CloudServiceInstance.builder() - .serviceInstance(buildServiceInstanceResource()) - .build(); - } - - private ServiceInstanceResource buildServiceInstanceResource() { - return ServiceInstanceResource.builder() - .id(SERVICE_INSTANCE_ID) - .name(SERVICE_INSTANCE_NAME) - .createdAt(DATE) - .updatedAt(DATE) - .dashboardUrl(DASHBOARD_URL) - .lastOperation(LastOperation.builder() - .createdAt(DATE) - .description(LAST_OPERATION_DESCRIPTION) - .state(LAST_OPERATION_STATE) - .type(LAST_OPERATION_TYPE) - .updatedAt(DATE) - .build()) - .maintenanceInfo(MaintenanceInfo.builder() - .description(MAINTENANCE_INFO_DESCRIPTION) - .version(MAINTENANCE_INFO_VERSION) - .build()) - .metadata(Metadata.builder() - .annotations(METADATA_ANNOTATIONS) - .labels(METADATA_LABELS) - .build()) - .relationships(ServiceInstanceRelationships.builder() - .space(ToOneRelationship.builder() - .data(Relationship.builder() - .id(SPACE_GUID) - .build()) - .build()) - .servicePlan(ToOneRelationship.builder() - .data(Relationship.builder() - .id(SERVICE_PLAN_GUID) - .build()) - .build()) - .build()) - .routeServiceUrl(ROUTE_SERVICE_URL) - .syslogDrainUrl(SYSLOG_DRAIN_URL) - .tags(TAGS) - .type(SERVICE_INSTANCE_TYPE) - .updateAvailable(UPDATE_AVAILABLE) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceKeyTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceKeyTest.java deleted file mode 100644 index e8c49dc920..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceKeyTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - -import org.cloudfoundry.client.v3.LastOperation; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingRelationships; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingResource; -import org.cloudfoundry.client.v3.servicebindings.ServiceBindingType; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; - -class RawCloudServiceKeyTest { - - private static final String SERVICE_NAME = "foo"; - private static final String NAME = "bar"; - private static final Map CREDENTIALS = buildTestCredentials(); - private static final CloudServiceInstance SERVICE_INSTANCE = ImmutableCloudServiceInstance.builder() - .name(SERVICE_NAME) - .build(); - private static final LastOperation LAST_OPERATION = buildLastOperation(); - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedServiceKey(), buildRawServiceKey()); - } - - private static CloudServiceKey buildExpectedServiceKey() { - return ImmutableCloudServiceKey.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) - .v3Metadata(RawCloudEntityTest.V3_METADATA) - .name(NAME) - .credentials(CREDENTIALS) - .serviceInstance(SERVICE_INSTANCE) - .serviceKeyOperation(ServiceCredentialBindingOperation.from(LAST_OPERATION)) - .build(); - } - - private static RawCloudServiceKey buildRawServiceKey() { - return ImmutableRawCloudServiceKey.builder() - .serviceBindingResource(buildTestResource()) - .serviceInstance(SERVICE_INSTANCE) - .credentials(CREDENTIALS) - .build(); - } - - private static ServiceBindingResource buildTestResource() { - return ServiceBindingResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .metadata(RawCloudEntityTest.V3_METADATA) - .name(NAME) - .type(ServiceBindingType.KEY) - .lastOperation(LAST_OPERATION) - .relationships(ServiceBindingRelationships.builder() - .serviceInstance(RawCloudEntityTest.buildToOneRelationship("")) - .build()) - .build(); - } - - private static Map buildTestCredentials() { - Map parameters = new HashMap<>(); - parameters.put("foo", "bar"); - parameters.put("baz", false); - parameters.put("qux", 3.141); - return parameters; - } - - private static LastOperation buildLastOperation() { - return LastOperation.builder() - .state(ServiceCredentialBindingOperation.State.SUCCEEDED.toString()) - .type(ServiceCredentialBindingOperation.Type.CREATE.toString()) - .createdAt(LocalDateTime.now() - .toString()) - .updatedAt(LocalDateTime.now() - .toString()) - .description("Service key created") - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceOfferingTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceOfferingTest.java deleted file mode 100644 index b80fd6d986..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceOfferingTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.serviceofferings.BrokerCatalog; -import org.cloudfoundry.client.v3.serviceofferings.Features; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingRelationships; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingResource; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceOffering; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceOffering; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServicePlan; - -public class RawCloudServiceOfferingTest { - - private static final String NAME = "postgresql"; - static final boolean AVAILABLE = true; - static final boolean BINDABLE = true; - static final boolean ALLOW_CONTEXT_UPDATES = false; - static final boolean BINDINGS_RETRIEVABLE = true; - static final boolean INSTANCES_RETRIEVABLE = true; - static final boolean PLAN_UPDATEABLE = false; - static final boolean SHAREABLE = true; - static final String DESCRIPTION = "description"; - static final String DOCUMENTATION_URL = "/documentation"; - static final String UNIQUE_ID = "unique-id"; - static final String SERVICE_BROKER_GUID = UUID.randomUUID() - .toString(); - private static final Map EXTRA = Map.of("key-metadata", "value-metadata"); - private static final List PLANS = buildTestServiceBindings(); - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedServiceOffering(), buildRawServiceOffering()); - } - - private static CloudServiceOffering buildExpectedServiceOffering() { - return ImmutableCloudServiceOffering.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .brokerId(SERVICE_BROKER_GUID) - .name(NAME) - .isAvailable(AVAILABLE) - .isBindable(BINDABLE) - .isShareable(SHAREABLE) - .description(DESCRIPTION) - .extra(EXTRA) - .docUrl(DOCUMENTATION_URL) - .uniqueId(UNIQUE_ID) - .servicePlans(PLANS) - .build(); - } - - private static RawCloudServiceOffering buildRawServiceOffering() { - return ImmutableRawCloudServiceOffering.builder() - .serviceOffering(buildTestServiceOffering()) - .servicePlans(PLANS) - .build(); - } - - public static ServiceOfferingResource buildTestServiceOffering() { - return ServiceOfferingResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .available(AVAILABLE) - .name(NAME) - .brokerCatalog(BrokerCatalog.builder() - .brokerCatalogId(UNIQUE_ID) - .features(Features.builder() - .bindable(BINDABLE) - .allowContextUpdates(ALLOW_CONTEXT_UPDATES) - .bindingsRetrievable(BINDINGS_RETRIEVABLE) - .instancesRetrievable(INSTANCES_RETRIEVABLE) - .planUpdateable(PLAN_UPDATEABLE) - .build()) - .metadata(EXTRA) - .build()) - .relationships(ServiceOfferingRelationships.builder() - .serviceBroker(ToOneRelationship.builder() - .data(Relationship.builder() - .id(SERVICE_BROKER_GUID) - .build()) - .build()) - .build()) - .shareable(SHAREABLE) - .description(DESCRIPTION) - .documentationUrl(DOCUMENTATION_URL) - .build(); - } - - private static List buildTestServiceBindings() { - return List.of(buildTestServiceBinding()); - } - - private static CloudServicePlan buildTestServiceBinding() { - return ImmutableCloudServicePlan.builder() - .name("v9.4-small") - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServicePlanTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServicePlanTest.java deleted file mode 100644 index f43f5e6a6a..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServicePlanTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Map; -import java.util.UUID; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.serviceplans.BrokerCatalog; -import org.cloudfoundry.client.v3.serviceplans.Features; -import org.cloudfoundry.client.v3.serviceplans.Schema; -import org.cloudfoundry.client.v3.serviceplans.Schemas; -import org.cloudfoundry.client.v3.serviceplans.ServiceInstanceSchema; -import org.cloudfoundry.client.v3.serviceplans.ServicePlan; -import org.cloudfoundry.client.v3.serviceplans.ServicePlanRelationships; -import org.cloudfoundry.client.v3.serviceplans.ServicePlanResource; -import org.cloudfoundry.client.v3.serviceplans.Visibility; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServicePlan; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServicePlan; - -public class RawCloudServicePlanTest { - - private static final String NAME = "v9.4-small"; - private static final String DESCRIPTION = "description"; - private static final Map EXTRA = Map.of("extra", "value"); - private static final String UNIQUE_ID = "unique-id"; - private static final boolean PUBLIC = true; - private static final boolean FREE = false; - private static final boolean PLAN_BINDABLE = true; - private static final boolean PLAN_UPDATABLE = true; - private static final boolean AVAILABLE = true; - private static final String SERVICE_OFFERING_ID = UUID.randomUUID() - .toString(); - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedPlan(), buildRawServicePlan()); - } - - private static CloudServicePlan buildExpectedPlan() { - return ImmutableCloudServicePlan.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .name(NAME) - .description(DESCRIPTION) - .extra(EXTRA) - .uniqueId(UNIQUE_ID) - .isPublic(PUBLIC) - .isFree(FREE) - .serviceOfferingId(SERVICE_OFFERING_ID) - .build(); - } - - private static RawCloudServicePlan buildRawServicePlan() { - return ImmutableRawCloudServicePlan.of(buildTestServicePlan(NAME)); - } - - public static ServicePlan buildTestServicePlan(String planName) { - return ServicePlanResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .available(AVAILABLE) - .name(planName) - .description(DESCRIPTION) - .brokerCatalog(BrokerCatalog.builder() - .metadata(EXTRA) - .brokerCatalogId(UNIQUE_ID) - .features(Features.builder() - .bindable(PLAN_BINDABLE) - .planUpdateable(PLAN_UPDATABLE) - .build()) - .build()) - .visibilityType(Visibility.PUBLIC) - .free(FREE) - .relationships(ServicePlanRelationships.builder() - .serviceOffering(ToOneRelationship.builder() - .data(Relationship.builder() - .id(SERVICE_OFFERING_ID) - .build()) - .build()) - .build()) - .schemas(Schemas.builder() - .serviceInstance(ServiceInstanceSchema.builder() - .create(Schema.builder() - .build()) - .build()) - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceTest.java deleted file mode 100644 index 8a4fec8ef0..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudServiceTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import java.util.Arrays; -import java.util.List; - -import org.cloudfoundry.client.v3.LastOperation; -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceResource; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; -import org.cloudfoundry.client.v3.serviceofferings.BrokerCatalog; -import org.cloudfoundry.client.v3.serviceofferings.Features; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingRelationships; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingResource; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation.State; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation.Type; - -class RawCloudServiceTest { - - private static final String NAME = "my-db"; - private static final String OFFERING_NAME = "postgresql"; - private static final String PLAN_NAME = "v9.4-small"; - private static final List TAGS = Arrays.asList("test-tag-1", "test-tag-2"); - private static final String LAST_OPERATION_TYPE_CREATE = "create"; - private static final String LAST_OPERATION_STATE_IN_PROGRESS = "in progress"; - private static final String LAST_OPERATION_STATE_SUCCEEDED = "succeeded"; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedService(), buildRawService()); - } - - @Test - void testDeriveWithUserProvidedService() { - RawCloudEntityTest.testDerive(buildExpectedUserProvidedService(), buildRawUserProvidedService()); - } - - private static CloudServiceInstance buildExpectedService() { - return ImmutableCloudServiceInstance.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .name(NAME) - .plan(PLAN_NAME) - .label(OFFERING_NAME) - .type(ServiceInstanceType.MANAGED) - .tags(TAGS) - .lastOperation(new ServiceOperation(Type.CREATE, "", State.IN_PROGRESS)) - .build(); - } - - private static CloudServiceInstance buildExpectedUserProvidedService() { - return ImmutableCloudServiceInstance.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .name(NAME) - .type(ServiceInstanceType.USER_PROVIDED) - .tags(TAGS) - .lastOperation(new ServiceOperation(Type.CREATE, "", State.SUCCEEDED)) - .build(); - } - - private static RawCloudServiceInstance buildRawService() { - return ImmutableRawCloudServiceInstance.builder() - .resource(buildTestResource(false, LastOperation.builder() - .type(LAST_OPERATION_TYPE_CREATE) - .state(LAST_OPERATION_STATE_IN_PROGRESS) - .build())) - .servicePlan(RawCloudServicePlanTest.buildTestServicePlan(PLAN_NAME)) - .serviceOffering(buildTestServiceOffering()) - .build(); - } - - private static RawCloudServiceInstance buildRawUserProvidedService() { - return ImmutableRawCloudServiceInstance.builder() - .resource(buildTestResource(true, LastOperation.builder() - .type(LAST_OPERATION_TYPE_CREATE) - .state(LAST_OPERATION_STATE_SUCCEEDED) - .build())) - .build(); - } - - private static ServiceInstanceResource buildTestResource(boolean isUserProvided, LastOperation lastOperation) { - ServiceInstanceResource.Builder serviceInstanceResourceBuilder = ServiceInstanceResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .name(NAME) - .type(ServiceInstanceType.MANAGED) - .addAllTags(TAGS) - .lastOperation(lastOperation); - if (isUserProvided) { - serviceInstanceResourceBuilder.type(ServiceInstanceType.USER_PROVIDED); - } - return serviceInstanceResourceBuilder.build(); - } - - private static ServiceOfferingResource buildTestServiceOffering() { - return ServiceOfferingResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .available(RawCloudServiceOfferingTest.AVAILABLE) - .name(OFFERING_NAME) - .brokerCatalog(BrokerCatalog.builder() - .brokerCatalogId(RawCloudServiceOfferingTest.UNIQUE_ID) - .features(Features.builder() - .bindable(RawCloudServiceOfferingTest.BINDABLE) - .allowContextUpdates(RawCloudServiceOfferingTest.ALLOW_CONTEXT_UPDATES) - .bindingsRetrievable(RawCloudServiceOfferingTest.BINDINGS_RETRIEVABLE) - .instancesRetrievable(RawCloudServiceOfferingTest.INSTANCES_RETRIEVABLE) - .planUpdateable(RawCloudServiceOfferingTest.PLAN_UPDATEABLE) - .build()) - .build()) - .relationships(ServiceOfferingRelationships.builder() - .serviceBroker(ToOneRelationship.builder() - .data(Relationship.builder() - .id(RawCloudServiceOfferingTest.SERVICE_BROKER_GUID) - .build()) - .build()) - .build()) - .shareable(RawCloudServiceOfferingTest.SHAREABLE) - .description(RawCloudServiceOfferingTest.DESCRIPTION) - .documentationUrl(RawCloudServiceOfferingTest.DOCUMENTATION_URL) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudStackTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudStackTest.java deleted file mode 100644 index ac4da5f8b2..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudStackTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.stacks.Stack; -import org.cloudfoundry.client.v3.stacks.StackResource; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudStack; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudStack; - -class RawCloudStackTest { - - private static final String NAME = "cflinuxfs3"; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedStack(), buildRawStack()); - } - - private static CloudStack buildExpectedStack() { - return ImmutableCloudStack.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_V3) - .name(NAME) - .build(); - } - - private static RawCloudStack buildRawStack() { - return ImmutableRawCloudStack.of(buildTestResource()); - } - - private static Stack buildTestResource() { - return StackResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .metadata(RawCloudEntityTest.V3_METADATA) - .name(NAME) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudTaskTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudTaskTest.java deleted file mode 100644 index 9d69a9da4c..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudTaskTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.tasks.Result; -import org.cloudfoundry.client.v3.tasks.Task; -import org.cloudfoundry.client.v3.tasks.TaskResource; -import org.cloudfoundry.client.v3.tasks.TaskState; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudTask; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudTask; - -class RawCloudTaskTest { - - private static final String NAME = "echo"; - private static final String COMMAND = "echo \"Hello!\""; - private static final int DISK_IN_MB = 128; - private static final int MEMORY_IN_MB = 256; - private static final String FAILURE_REASON = "blabla"; - private static final String DROPLET_ID = "8ff24465-823b-4a55-85ee-a680f2c743cd"; - private static final int SEQUENCE_ID = 3; - private static final TaskState STATE = TaskState.FAILED; - - private static final CloudTask.State EXPECTED_STATE = CloudTask.State.FAILED; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedTask(), buildRawTask()); - } - - private static CloudTask buildExpectedTask() { - return ImmutableCloudTask.builder() - .metadata(RawCloudEntityTest.EXPECTED_METADATA_PARSED_FROM_V3_RESOURCE) - .name(NAME) - .command(COMMAND) - .limits(ImmutableCloudTask.ImmutableLimits.builder() - .disk(DISK_IN_MB) - .memory(MEMORY_IN_MB) - .build()) - .result(ImmutableCloudTask.ImmutableResult.builder() - .failureReason(FAILURE_REASON) - .build()) - .state(EXPECTED_STATE) - .build(); - } - - private static RawCloudTask buildRawTask() { - return ImmutableRawCloudTask.of(buildTestResource()); - } - - private static Task buildTestResource() { - return TaskResource.builder() - .id(RawCloudEntityTest.GUID_STRING) - .createdAt(RawCloudEntityTest.CREATED_AT_STRING) - .updatedAt(RawCloudEntityTest.UPDATED_AT_STRING) - .name(NAME) - .command(COMMAND) - .diskInMb(DISK_IN_MB) - .memoryInMb(MEMORY_IN_MB) - .state(STATE) - .result(buildTestResult()) - .dropletId(DROPLET_ID) - .sequenceId(SEQUENCE_ID) - .build(); - } - - private static Result buildTestResult() { - return Result.builder() - .failureReason(FAILURE_REASON) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawInstancesInfoTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawInstancesInfoTest.java deleted file mode 100644 index 2051f7a207..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawInstancesInfoTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.applications.GetApplicationProcessStatisticsResponse; -import org.cloudfoundry.client.v3.processes.ProcessState; -import org.cloudfoundry.client.v3.processes.ProcessStatisticsResource; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableInstanceInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableInstancesInfo; -import org.cloudfoundry.multiapps.controller.client.facade.domain.InstanceState; -import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; -import org.junit.jupiter.api.Test; - -class RawInstancesInfoTest { - - @Test - void testDeriveWithTrueRoutable() { - RawCloudEntityTest.testDerive(buildExpectedInstancesInfo(true), buildActualInstancesInfo("true")); - } - - @Test - void testDeriveWithFalseRoutable() { - RawCloudEntityTest.testDerive(buildExpectedInstancesInfo(false), buildActualInstancesInfo("false")); - } - - private InstancesInfo buildExpectedInstancesInfo(boolean expectedRoutable) { - return ImmutableInstancesInfo.builder() - .addInstance(ImmutableInstanceInfo.builder() - .index(0) - .state(InstanceState.RUNNING) - .isRoutable(expectedRoutable) - .build()) - .build(); - } - - private RawInstancesInfo buildActualInstancesInfo(String routable) { - return ImmutableRawInstancesInfo.builder() - .processStatisticsResponse(getApplicationProcessStatisticsResponse(routable)) - .build(); - } - - private GetApplicationProcessStatisticsResponse getApplicationProcessStatisticsResponse(String routable) { - return GetApplicationProcessStatisticsResponse.builder() - .resource(ProcessStatisticsResource.builder() - .index(0) - .details("Instance is in running state") - .diskQuota(1024L) - .state(ProcessState.RUNNING) - .memoryQuota(1024L) - .type("web") - .uptime(9042L) - .fileDescriptorQuota(1024L) - .routable(routable) - .host("10.244.16.10") - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawUserRoleTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawUserRoleTest.java deleted file mode 100644 index bc650d4603..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawUserRoleTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.adapters; - -import org.cloudfoundry.client.v3.Relationship; -import org.cloudfoundry.client.v3.ToOneRelationship; -import org.cloudfoundry.client.v3.roles.RoleRelationships; -import org.cloudfoundry.client.v3.roles.RoleResource; -import org.cloudfoundry.client.v3.roles.RoleType; -import org.junit.jupiter.api.Test; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.UserRole; - -class RawUserRoleTest { - - private static final String USER_ROLE_GUID = "c94b4588-3431-11ed-a261-0242ac120002"; - private static final String USER_GUID = "328eaf4e-3432-11ed-a261-0242ac120002"; - private static final String DATE = "2022-01-01T13:35:11Z"; - private static final RoleType USER_ROLE_TYPE = RoleType.SPACE_DEVELOPER; - - @Test - void testDerive() { - RawCloudEntityTest.testDerive(buildExpectedUserRole(), buildActualUserRole()); - } - - private UserRole buildExpectedUserRole() { - return UserRole.SPACE_DEVELOPER; - } - - private RawUserRole buildActualUserRole() { - return ImmutableRawUserRole.builder() - .roleResource(buildRoleResource()) - .build(); - } - - private RoleResource buildRoleResource() { - return RoleResource.builder() - .createdAt(DATE) - .id(USER_ROLE_GUID) - .type(USER_ROLE_TYPE) - .relationships(RoleRelationships.builder() - .user(ToOneRelationship.builder() - .data(Relationship.builder() - .id(USER_GUID) - .build()) - .build()) - .build()) - .build(); - } - -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImplTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImplTest.java deleted file mode 100644 index 0519cbecce..0000000000 --- a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImplTest.java +++ /dev/null @@ -1,309 +0,0 @@ -package org.cloudfoundry.multiapps.controller.client.facade.rest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Stream; - -import org.cloudfoundry.client.CloudFoundryClient; -import org.cloudfoundry.client.v3.BuildpackData; -import org.cloudfoundry.client.v3.ClientV3Exception; -import org.cloudfoundry.client.v3.Lifecycle; -import org.cloudfoundry.client.v3.LifecycleType; -import org.cloudfoundry.client.v3.Pagination; -import org.cloudfoundry.client.v3.applications.ApplicationResource; -import org.cloudfoundry.client.v3.applications.ApplicationState; -import org.cloudfoundry.client.v3.applications.ApplicationsV3; -import org.cloudfoundry.client.v3.applications.GetApplicationRequest; -import org.cloudfoundry.client.v3.applications.GetApplicationResponse; -import org.cloudfoundry.client.v3.applications.ListApplicationsRequest; -import org.cloudfoundry.client.v3.applications.ListApplicationsResponse; -import org.cloudfoundry.client.v3.applications.UpdateApplicationRequest; -import org.cloudfoundry.client.v3.applications.UpdateApplicationResponse; -import org.cloudfoundry.client.v3.serviceofferings.GetServiceOfferingRequest; -import org.cloudfoundry.client.v3.serviceofferings.GetServiceOfferingResponse; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOffering; -import org.cloudfoundry.client.v3.serviceofferings.ServiceOfferingsV3; -import org.cloudfoundry.client.v3.serviceplans.GetServicePlanRequest; -import org.cloudfoundry.client.v3.serviceplans.GetServicePlanResponse; -import org.cloudfoundry.client.v3.serviceplans.ServicePlan; -import org.cloudfoundry.client.v3.serviceplans.ServicePlansV3; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.springframework.http.HttpStatus; - -import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.RawCloudServiceOfferingTest; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.RawCloudServicePlanTest; -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudSpace; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; -import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudSpace; - -import reactor.core.publisher.Mono; - -class CloudControllerRestClientImplTest { - - private static final String SERVICE_PLAN_GUID = "1803e5a7-40c7-438e-b2be-e2045c9b7cda"; - private static final String SERVICE_INSTANCE_GUID = "26949ebb-a624-35c0-000-1110a01f1880"; - private static final String SERVICE_OFFERING_GUID = "1803e5a7-40c7-438e-b2be-e2045c9b7cda"; - private static final String PLAN_NAME = "test-plan"; - private static final String OLD_APPLICATION_NAME = "old-app-name"; - private static final String NEW_APPLICATION_NAME = "new-app-name"; - private static final String APPLICATION_CREATED_AT = "2024-01-26T10:00:00"; - private static final UUID APPLICATION_GUID = UUID.randomUUID(); - private static final UUID SPACE_GUID = UUID.randomUUID(); - - @Mock - private CloudFoundryClient delegate; - private CloudControllerRestClientImpl controllerClient; - - @BeforeEach - public void setUp() throws Exception { - MockitoAnnotations.openMocks(this) - .close(); - controllerClient = new CloudControllerRestClientImpl(delegate); - } - - @Test - void testGetServiceOffering() { - GetServiceOfferingRequest request = GetServiceOfferingRequest.builder() - .serviceOfferingId(SERVICE_PLAN_GUID) - .build(); - GetServiceOfferingResponse response = GetServiceOfferingResponse.builder() - .from(RawCloudServiceOfferingTest.buildTestServiceOffering()) - .build(); - - ServiceOfferingsV3 serviceOfferingsV3 = Mockito.mock(ServiceOfferingsV3.class); - Mockito.when(delegate.serviceOfferingsV3()) - .thenReturn(serviceOfferingsV3); - Mockito.when(serviceOfferingsV3.get(request)) - .thenReturn(Mono.just(response)); - - ServiceOffering serviceOffering = controllerClient.getServiceOffering(SERVICE_PLAN_GUID) - .block(); - - assertEquals(response, serviceOffering); - } - - public static Stream testGetServiceOfferingWithError() { - return Stream.of( -// @formatter:off - Arguments.of(HttpStatus.FORBIDDEN.value(), - "403 Forbidden: Service offering with guid \"1803e5a7-40c7-438e-b2be-e2045c9b7cda\" is not available."), - Arguments.of(HttpStatus.NOT_FOUND.value(), - "404 Not Found: Service offering with guid \"1803e5a7-40c7-438e-b2be-e2045c9b7cda\" not found.") -// @formatter:on - ); - } - - @ParameterizedTest - @MethodSource - void testGetServiceOfferingWithError(int errorCode, String expectedErrorMessage) { - GetServiceOfferingRequest request = GetServiceOfferingRequest.builder() - .serviceOfferingId(SERVICE_OFFERING_GUID) - .build(); - ServiceOfferingsV3 serviceOfferingsV3 = Mockito.mock(ServiceOfferingsV3.class); - Mockito.when(delegate.serviceOfferingsV3()) - .thenReturn(serviceOfferingsV3); - Mockito.when(serviceOfferingsV3.get(request)) - .thenReturn(Mono.error(clientV3Exception(errorCode))); - Exception cloudControllerException = assertThrows(CloudOperationException.class, - () -> controllerClient.getServiceOffering(SERVICE_OFFERING_GUID) - .block()); - assertEquals(expectedErrorMessage, cloudControllerException.getMessage()); - } - - public static Stream testGetServicePlanWithError() { - return Stream.of( -// @formatter:off - Arguments.of(HttpStatus.FORBIDDEN.value(), - "403 Forbidden: Service plan with guid \"1803e5a7-40c7-438e-b2be-e2045c9b7cda\" is not available for service instance \"26949ebb-a624-35c0-000-1110a01f1880\"."), - Arguments.of(HttpStatus.NOT_FOUND.value(), - "404 Not Found: Service plan with guid \"1803e5a7-40c7-438e-b2be-e2045c9b7cda\" for service instance with name \"26949ebb-a624-35c0-000-1110a01f1880\" was not found.") -// @formatter:on - ); - } - - @ParameterizedTest - @MethodSource - void testGetServicePlanWithError(int errorCode, String expectedErrorMessage) { - GetServicePlanRequest request = GetServicePlanRequest.builder() - .servicePlanId(SERVICE_PLAN_GUID) - .build(); - - ServicePlansV3 servicePlans = Mockito.mock(ServicePlansV3.class); - Mockito.when(delegate.servicePlansV3()) - .thenReturn(servicePlans); - Mockito.when(servicePlans.get(request)) - .thenReturn(Mono.error(clientV3Exception(errorCode))); - - Exception cloudControllerException = assertThrows(CloudOperationException.class, - () -> controllerClient.getServicePlanResource(SERVICE_PLAN_GUID, - SERVICE_INSTANCE_GUID) - .block()); - assertEquals(expectedErrorMessage, cloudControllerException.getMessage()); - } - - @Test - void testGetServicePlanResource() { - GetServicePlanRequest request = GetServicePlanRequest.builder() - .servicePlanId(SERVICE_PLAN_GUID) - .build(); - GetServicePlanResponse response = GetServicePlanResponse.builder() - .from(RawCloudServicePlanTest.buildTestServicePlan(PLAN_NAME)) - .build(); - - ServicePlansV3 servicePlans = Mockito.mock(ServicePlansV3.class); - Mockito.when(delegate.servicePlansV3()) - .thenReturn(servicePlans); - Mockito.when(servicePlans.get(request)) - .thenReturn(Mono.just(response)); - - ServicePlan servicePlanResource = controllerClient.getServicePlanResource(SERVICE_PLAN_GUID, SERVICE_INSTANCE_GUID) - .block(); - - assertEquals(response, servicePlanResource); - } - - private static Stream testRenameApplication() { - return Stream.of( - // (1) Successful application rename - Arguments.of(Mono.just(UpdateApplicationResponse.builder() - .name(NEW_APPLICATION_NAME) - .id(APPLICATION_GUID.toString()) - .createdAt(APPLICATION_CREATED_AT) - .state(ApplicationState.STARTED) - .lifecycle(getApplicationLifecycle()) - .build()), - null, false), - // (2) Controller's API returns 503 and rename was actually done - Arguments.of(Mono.error(new ClientV3Exception(503, - List.of(org.cloudfoundry.client.v3.Error.builder() - .code(170015) - .title("CF-RunnerUnavailable") - .detail("Runner is unavailable") - .build()))), - Mono.just(GetApplicationResponse.builder() - .name(NEW_APPLICATION_NAME) - .id(APPLICATION_GUID.toString()) - .createdAt(APPLICATION_CREATED_AT) - .state(ApplicationState.STARTED) - .lifecycle(getApplicationLifecycle()) - .build()), - false), - // (3) Controller's API fails with 500 and application wasn't renamed - Arguments.of(Mono.error(new ClientV3Exception(500, Collections.emptyList())), null, true), - // (4) Controller's API returns 503 and while make request to get current application name the request fails - Arguments.of(Mono.error(new ClientV3Exception(503, - List.of(org.cloudfoundry.client.v3.Error.builder() - .code(170015) - .title("CF-RunnerUnavailable") - .detail("Runner is unavailable") - .build()))), - Mono.error(new ClientV3Exception(500, Collections.emptyList())), true), - // (5) Controller's API returns 503 and application wasn't renamed - Arguments.of(Mono.error(new ClientV3Exception(503, - List.of(org.cloudfoundry.client.v3.Error.builder() - .code(170015) - .title("CF-RunnerUnavailable") - .detail("Runner is unavailable") - .build()))), - Mono.just(GetApplicationResponse.builder() - .name(OLD_APPLICATION_NAME) - .id(APPLICATION_GUID.toString()) - .createdAt(APPLICATION_CREATED_AT) - .state(ApplicationState.STARTED) - .lifecycle(getApplicationLifecycle()) - .build()), - true)); - } - - private static Lifecycle getApplicationLifecycle() { - return Lifecycle.builder() - .type(LifecycleType.BUILDPACK) - .data(BuildpackData.builder() - .build()) - .build(); - } - - @ParameterizedTest - @MethodSource - void testRenameApplication(Mono updateApplicationResponse, - Mono getApplicationResponse, boolean expectedFailure) { - ApplicationsV3 applicationsV3 = Mockito.mock(ApplicationsV3.class); - var updateAppRequest = UpdateApplicationRequest.builder() - .applicationId(APPLICATION_GUID.toString()) - .name(NEW_APPLICATION_NAME) - .build(); - prepareClientDelegate(applicationsV3, updateAppRequest, updateApplicationResponse, getApplicationResponse); - - if (expectedFailure) { - assertThrows(ClientV3Exception.class, () -> controllerClient.rename(OLD_APPLICATION_NAME, NEW_APPLICATION_NAME)); - return; - } - controllerClient.rename(OLD_APPLICATION_NAME, NEW_APPLICATION_NAME); - - Mockito.verify(applicationsV3, Mockito.atMostOnce()) - .update(updateAppRequest); - } - - private void prepareClientDelegate(ApplicationsV3 applicationsV3, UpdateApplicationRequest updateAppRequest, - Mono updateApplicationResponse, - Mono getApplicationResponse) { - initControllerClientWithTargetSpace(SPACE_GUID); - - Mockito.when(delegate.applicationsV3()) - .thenReturn(applicationsV3); - var listAppsRequest = ListApplicationsRequest.builder() - .spaceId(SPACE_GUID.toString()) - .name(OLD_APPLICATION_NAME) - .page(1) - .build(); - - var listAppsResponse = ListApplicationsResponse.builder() - .resource(ApplicationResource.builder() - .name(OLD_APPLICATION_NAME) - .id(APPLICATION_GUID.toString()) - .createdAt(APPLICATION_CREATED_AT) - .state(ApplicationState.STARTED) - .lifecycle(getApplicationLifecycle()) - .build()) - .pagination(Pagination.builder() - .totalResults(1) - .totalPages(1) - .build()) - .build(); - Mockito.when(applicationsV3.list(listAppsRequest)) - .thenReturn(Mono.just(listAppsResponse)); - Mockito.when(applicationsV3.update(updateAppRequest)) - .thenReturn(updateApplicationResponse); - var getAppRequest = GetApplicationRequest.builder() - .applicationId(APPLICATION_GUID.toString()) - .build(); - - Mockito.when(applicationsV3.get(getAppRequest)) - .thenReturn(getApplicationResponse); - } - - private void initControllerClientWithTargetSpace(UUID spaceGuid) { - CloudSpace target = ImmutableCloudSpace.builder() - .metadata(ImmutableCloudMetadata.builder() - .guid(spaceGuid) - .build()) - .build(); - this.controllerClient = new CloudControllerRestClientImpl(delegate, target); - } - - private ClientV3Exception clientV3Exception(int statusCode) { - return new ClientV3Exception(statusCode, Collections.emptyList()); - } -} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3ClientPageUriTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3ClientPageUriTest.java new file mode 100644 index 0000000000..a9a385f183 --- /dev/null +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerV3ClientPageUriTest.java @@ -0,0 +1,39 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class CloudControllerV3ClientPageUriTest { + + @Test + void testPageUriAddsPageWhenAbsentAndKeepsExistingQuery() { + String uri = CloudControllerV3Client.pageUri("/v3/apps?per_page=5000&space_guids=abc", 3); + + Assertions.assertTrue(uri.contains("page=3"), uri); + Assertions.assertTrue(uri.contains("per_page=5000"), uri); + Assertions.assertTrue(uri.contains("space_guids=abc"), uri); + } + + @Test + void testPageUriReplacesExistingPageParam() { + String uri = CloudControllerV3Client.pageUri("/v3/apps?per_page=5000&page=1", 4); + + Assertions.assertTrue(uri.contains("page=4"), uri); + Assertions.assertFalse(uri.contains("page=1"), uri); + Assertions.assertTrue(uri.contains("per_page=5000"), uri); + } + + @Test + void testPageUriPreservesMultipleFilters() { + String first = "/v3/service_credential_bindings?per_page=5000&type=key&service_instance_guids=g1&names=n1"; + + String uri = CloudControllerV3Client.pageUri(first, 2); + + Assertions.assertTrue(uri.contains("page=2"), uri); + Assertions.assertTrue(uri.contains("type=key"), uri); + Assertions.assertTrue(uri.contains("service_instance_guids=g1"), uri); + Assertions.assertTrue(uri.contains("names=n1"), uri); + Assertions.assertTrue(uri.contains("per_page=5000"), uri); + } + +} diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ReactiveFanOutTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ReactiveFanOutTest.java new file mode 100644 index 0000000000..752a9e8c3f --- /dev/null +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/rest/ReactiveFanOutTest.java @@ -0,0 +1,107 @@ +package org.cloudfoundry.multiapps.controller.client.facade.rest; + +import java.util.List; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; + +import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.springframework.http.HttpStatus; + +class ReactiveFanOutTest { + + @Test + void testMapConcurrentlyReturnsEmptyWhenEmpty() { + Assertions.assertEquals(List.of(), ReactiveFanOut.mapConcurrently(List.of(), x -> x)); + } + + @Test + void testMapConcurrentlyShortCircuitsWhenSingleElement() { + List result = ReactiveFanOut.mapConcurrently(List.of("a"), s -> s + "!"); + + Assertions.assertEquals(List.of("a!"), result); + } + + @Test + void testMapConcurrentlyResultPreserveInputOrderEvenWhenTasksFinishOutOfOrder() { + List input = IntStream.rangeClosed(1, 20) + .boxed() + .toList(); + + List result = ReactiveFanOut.mapConcurrently(input, i -> { + sleep((20 - i)); + return i * 10; + }); + + List expected = input.stream() + .map(i -> i * 10) + .toList(); + Assertions.assertEquals(expected, result); + } + + @Test + @Timeout(10) + void testMapConcurrentlyWorkRunsConcurrently() throws Exception { + int n = 8; + // If the mapper ran sequentially, the barrier (which needs all n parties) would never trip and the test would time out. + CyclicBarrier barrier = new CyclicBarrier(n); + List input = IntStream.range(0, n) + .boxed() + .toList(); + + List result = ReactiveFanOut.mapConcurrently(input, i -> { + try { + barrier.await(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + return i; + }); + + Assertions.assertEquals(input, result); + } + + @Test + void testFirstFailurePropagatesUnwrapped() { + List input = List.of(1, 2, 3, 4); + + CloudOperationException thrown = Assertions.assertThrows(CloudOperationException.class, + () -> ReactiveFanOut.mapConcurrently(input, i -> { + if (i == 3) { + throw new CloudOperationException(HttpStatus.BAD_GATEWAY); + } + return i; + })); + + Assertions.assertEquals(HttpStatus.BAD_GATEWAY, thrown.getStatusCode()); + } + + @Test + void testMapperInvokedOncePerItem() { + List input = IntStream.range(0, 50) + .boxed() + .toList(); + AtomicInteger invocations = new AtomicInteger(); + + List result = ReactiveFanOut.mapConcurrently(input, i -> { + invocations.incrementAndGet(); + return i; + }); + + Assertions.assertEquals(input, result); + Assertions.assertEquals(input.size(), invocations.get()); + } + + private static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + Thread.currentThread() + .interrupt(); + } + } + +} diff --git a/multiapps-controller-core/pom.xml b/multiapps-controller-core/pom.xml index a7f67cbb8a..49c37e727a 100644 --- a/multiapps-controller-core/pom.xml +++ b/multiapps-controller-core/pom.xml @@ -103,6 +103,14 @@ multiapps-controller-core-test test
+ + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + test + org.cloudfoundry.multiapps multiapps-controller-client diff --git a/multiapps-controller-core/src/main/java/module-info.java b/multiapps-controller-core/src/main/java/module-info.java index b00243ee13..d24aaceea8 100644 --- a/multiapps-controller-core/src/main/java/module-info.java +++ b/multiapps-controller-core/src/main/java/module-info.java @@ -45,8 +45,6 @@ requires transitive org.cloudfoundry.multiapps.controller.client; requires transitive org.cloudfoundry.multiapps.controller.persistence; requires transitive org.cloudfoundry.multiapps.mta; - - requires org.cloudfoundry.client; requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.core; requires com.fasterxml.jackson.databind; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/CloudControllerClientFactory.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/CloudControllerClientFactory.java index 91fd2fe929..07a0a572b6 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/CloudControllerClientFactory.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/CloudControllerClientFactory.java @@ -68,8 +68,7 @@ public CloudControllerClient createClient(OAuth2AccessTokenWithAdditionalInfo to CloudCredentials credentials = createCredentials(token); oAuthClient.init(credentials); - var spaceClient = clientFactory.getCloudFoundryClientFactory() - .createSpaceClient(configuration.getControllerUrl(), oAuthClient, requestTags); + var spaceClient = clientFactory.createSpaceClient(configuration.getControllerUrl(), oAuthClient, requestTags); CloudSpace target = spaceClient.getSpace(UUID.fromString(spaceId)); CloudControllerRestClient controllerClient = clientFactory.createClient(configuration.getControllerUrl(), credentials, target, @@ -82,8 +81,7 @@ public CloudSpaceClient createSpaceClient(OAuth2AccessTokenWithAdditionalInfo to CloudCredentials credentials = createCredentials(token); oAuthClient.init(credentials); var requestTags = buildRequestTags(StringUtils.EMPTY); - return clientFactory.getCloudFoundryClientFactory() - .createSpaceClient(configuration.getControllerUrl(), oAuthClient, requestTags); + return clientFactory.createSpaceClient(configuration.getControllerUrl(), oAuthClient, requestTags); } public LogCacheClient createLogCacheClient(OAuth2AccessTokenWithAdditionalInfo token, String correlationId) { @@ -91,8 +89,7 @@ public LogCacheClient createLogCacheClient(OAuth2AccessTokenWithAdditionalInfo t CloudCredentials credentials = createCredentials(token); oAuthClient.init(credentials); var requestTags = buildRequestTags(correlationId); - return clientFactory.getCloudFoundryClientFactory() - .createLogCacheClient(configuration.getControllerUrl(), oAuthClient, requestTags); + return clientFactory.createLogCacheClient(configuration.getControllerUrl(), oAuthClient, requestTags); } private CloudCredentials createCredentials(OAuth2AccessTokenWithAdditionalInfo token) { diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CloudEntityResourceMapper.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CloudEntityResourceMapper.java index 8d01fe7eca..228cfaca71 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CloudEntityResourceMapper.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CloudEntityResourceMapper.java @@ -11,8 +11,8 @@ import java.util.Optional; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClient.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClient.java index 977ddae39b..d9e6a303f3 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClient.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClient.java @@ -7,7 +7,7 @@ import java.util.UUID; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.CloudCredentials; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataLabels; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/processor/MtaMetadataParser.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/processor/MtaMetadataParser.java index f9503d54ee..290ed3b8ae 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/processor/MtaMetadataParser.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/processor/MtaMetadataParser.java @@ -7,7 +7,7 @@ import jakarta.inject.Inject; import jakarta.inject.Named; import org.apache.commons.lang3.StringUtils; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEntity; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/util/MtaMetadataUtil.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/util/MtaMetadataUtil.java index 59b674ab9e..4d6d69572c 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/util/MtaMetadataUtil.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/metadata/util/MtaMetadataUtil.java @@ -4,7 +4,7 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudEntity; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataAnnotations; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataLabels; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ApplicationMetadataBuilder.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ApplicationMetadataBuilder.java index 44f9dee90b..aea8248cab 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ApplicationMetadataBuilder.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ApplicationMetadataBuilder.java @@ -5,7 +5,7 @@ import java.util.TreeMap; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.common.util.MapUtil; import org.cloudfoundry.multiapps.controller.core.Constants; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/MtaMetadataBuilder.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/MtaMetadataBuilder.java index 9b9a0d18db..a392eafb31 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/MtaMetadataBuilder.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/MtaMetadataBuilder.java @@ -1,8 +1,8 @@ package org.cloudfoundry.multiapps.controller.core.cf.v2; import org.apache.commons.lang3.StringUtils; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.Metadata.Builder; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata.Builder; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataAnnotations; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataLabels; import org.cloudfoundry.multiapps.controller.core.cf.metadata.util.MtaMetadataUtil; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeyMetadataBuilder.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeyMetadataBuilder.java index ae13c007af..e9ccf2fcc0 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeyMetadataBuilder.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeyMetadataBuilder.java @@ -1,7 +1,7 @@ package org.cloudfoundry.multiapps.controller.core.cf.v2; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.Metadata.Builder; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata.Builder; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataLabels; import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeysCloudModelBuilder.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeysCloudModelBuilder.java index ef5cb4a984..1ad059b028 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeysCloudModelBuilder.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceKeysCloudModelBuilder.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.Metadata; import org.cloudfoundry.multiapps.common.ContentException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceKey; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceMetadataBuilder.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceMetadataBuilder.java index 2bbc6befb9..f80768cd03 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceMetadataBuilder.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServiceMetadataBuilder.java @@ -3,8 +3,8 @@ import java.util.Map; import java.util.TreeMap; -import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.client.v3.Metadata.Builder; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata.Builder; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.common.util.MapUtil; import org.cloudfoundry.multiapps.controller.core.Constants; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java index 90abef06be..70e33a2be6 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/cf/v2/ServicesCloudModelBuilder.java @@ -7,7 +7,7 @@ import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClientTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClientTest.java index 670dcfaec3..ffddd0f832 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClientTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/clients/CustomServiceKeysClientTest.java @@ -6,8 +6,8 @@ import java.util.List; import java.util.UUID; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.CloudCredentials; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.core.model.DeployedMtaService; import org.cloudfoundry.multiapps.controller.core.model.DeployedMtaServiceKey; diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/detect/DeployedMtaDetectorTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/detect/DeployedMtaDetectorTest.java index e61ff34873..8d5230fb38 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/detect/DeployedMtaDetectorTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/cf/detect/DeployedMtaDetectorTest.java @@ -9,7 +9,7 @@ import java.util.stream.Stream; import com.fasterxml.jackson.core.type.TypeReference; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.test.Tester; import org.cloudfoundry.multiapps.common.test.Tester.Expectation; diff --git a/multiapps-controller-process/src/main/java/module-info.java b/multiapps-controller-process/src/main/java/module-info.java index 6b87de4bd9..36fdbb9c69 100644 --- a/multiapps-controller-process/src/main/java/module-info.java +++ b/multiapps-controller-process/src/main/java/module-info.java @@ -22,8 +22,6 @@ requires transitive flowable.engine; requires transitive org.cloudfoundry.multiapps.controller.api; requires transitive org.cloudfoundry.multiapps.controller.core; - - requires org.cloudfoundry.client; requires com.fasterxml.jackson.core; requires com.fasterxml.jackson.databind; requires com.google.common; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java index afb3fbdce1..60f0909f3c 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java @@ -8,7 +8,6 @@ import java.util.Set; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.ApplicationServicesUpdateCallback; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.ServiceBindingOperationCallback; @@ -32,6 +31,7 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.DockerInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.DropletInfo; import org.cloudfoundry.multiapps.controller.client.facade.domain.InstancesInfo; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ServicePlanVisibility; import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; import org.cloudfoundry.multiapps.controller.client.facade.domain.Upload; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OrphanedDataCleaner.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OrphanedDataCleaner.java index ef348f7181..9b8fbabb61 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OrphanedDataCleaner.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/jobs/OrphanedDataCleaner.java @@ -8,8 +8,10 @@ import org.cloudfoundry.multiapps.controller.client.facade.CloudCredentials; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; -import org.cloudfoundry.multiapps.controller.client.facade.adapters.ImmutableCloudFoundryClientFactory; +import org.cloudfoundry.multiapps.controller.client.facade.oauth2.OAuthClient; +import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudControllerRestClientFactory; import org.cloudfoundry.multiapps.controller.client.facade.rest.CloudSpaceClient; +import org.cloudfoundry.multiapps.controller.client.facade.rest.ImmutableCloudControllerRestClientFactory; import org.cloudfoundry.multiapps.controller.core.auditlogging.MtaConfigurationPurgerAuditLog; import org.cloudfoundry.multiapps.controller.core.cf.OAuthClientFactory; import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration; @@ -97,13 +99,13 @@ protected void initSpaceClient() { CloudCredentials cloudCredentials = new CloudCredentials(configuration.getGlobalAuditorUser(), configuration.getGlobalAuditorPassword(), SecurityUtil.CLIENT_ID, SecurityUtil.CLIENT_SECRET, configuration.getGlobalAuditorOrigin()); - var clientFactory = ImmutableCloudFoundryClientFactory.builder() - .connectTimeout(Duration.ofMinutes(5)) - .responseTimeout(Duration.ofMinutes(5)) - .connectionPoolSize(1) - .threadPoolSize(1) - .build(); - var oauthClient = oAuthClientFactory.createOAuthClient(); + CloudControllerRestClientFactory clientFactory = ImmutableCloudControllerRestClientFactory.builder() + .connectTimeout(Duration.ofMinutes(5)) + .responseTimeout(Duration.ofMinutes(5)) + .connectionPoolSize(1) + .threadPoolSize(1) + .build(); + OAuthClient oauthClient = oAuthClientFactory.createOAuthClient(); oauthClient.init(cloudCredentials); spaceClient = clientFactory.createSpaceClient(configuration.getControllerUrl(), oauthClient, Collections.emptyMap()); } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildApplicationDeployModelStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildApplicationDeployModelStep.java index dd6569a455..1bcfd82f39 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildApplicationDeployModelStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildApplicationDeployModelStep.java @@ -8,7 +8,7 @@ import jakarta.inject.Inject; import jakarta.inject.Named; import org.apache.commons.lang3.StringUtils; -import org.cloudfoundry.client.v3.processes.HealthCheckType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudRoute; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableStaging; import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ClearMtaMetadataBaseStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ClearMtaMetadataBaseStep.java index 11a92109fd..b4cf434a2c 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ClearMtaMetadataBaseStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/ClearMtaMetadataBaseStep.java @@ -4,7 +4,7 @@ import java.util.UUID; import org.apache.commons.collections4.MapUtils; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStep.java index da5a65dc5e..bcb90f5197 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStep.java @@ -16,13 +16,13 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.ObjectUtils; -import org.cloudfoundry.client.v3.Metadata; import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStep.java index 4a0dfe41ad..d0fe2541e3 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStep.java @@ -8,7 +8,7 @@ import jakarta.inject.Named; import org.apache.commons.collections4.ListUtils; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStep.java index ec99aa294e..3fcf4a2856 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStep.java @@ -3,7 +3,7 @@ import java.text.MessageFormat; import jakarta.inject.Named; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataAnnotations; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStep.java index 78bff75200..7d93f193a0 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStep.java @@ -1,7 +1,7 @@ package org.cloudfoundry.multiapps.controller.process.steps; import jakarta.inject.Named; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataAnnotations; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataLabels; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/StepsUtil.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/StepsUtil.java index 8a1036276a..d5058281c8 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/StepsUtil.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/StepsUtil.java @@ -14,7 +14,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.lang3.StringUtils; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AsyncJobToAsyncExecutionStateAdapter.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AsyncJobToAsyncExecutionStateAdapter.java index bad6812d53..46e6bc95ed 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AsyncJobToAsyncExecutionStateAdapter.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/AsyncJobToAsyncExecutionStateAdapter.java @@ -3,8 +3,8 @@ import java.text.MessageFormat; import java.util.function.Consumer; -import org.cloudfoundry.client.v3.jobs.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.steps.AsyncExecutionState; import org.immutables.value.Value; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandler.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandler.java index 9fb4a67889..3127360fa3 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandler.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandler.java @@ -9,15 +9,16 @@ import jakarta.inject.Inject; import jakarta.inject.Named; -import org.cloudfoundry.client.v3.Metadata; import org.cloudfoundry.multiapps.controller.api.model.ImmutableOperation; import org.cloudfoundry.multiapps.controller.api.model.Operation; import org.cloudfoundry.multiapps.controller.api.model.Operation.State; import org.cloudfoundry.multiapps.controller.api.model.ProcessType; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.core.auditlogging.CloudLoggingServiceConfigurationAuditLog; import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientProvider; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataAnnotations; +import org.cloudfoundry.multiapps.controller.core.cloudlogging.CloudLoggingServiceClient; import org.cloudfoundry.multiapps.controller.core.model.DeployedMta; import org.cloudfoundry.multiapps.controller.core.model.DeployedMtaApplication; import org.cloudfoundry.multiapps.controller.core.util.LoggingUtil; @@ -25,13 +26,12 @@ import org.cloudfoundry.multiapps.controller.persistence.model.HistoricOperationEvent; import org.cloudfoundry.multiapps.controller.persistence.model.ImmutableHistoricOperationEvent; import org.cloudfoundry.multiapps.controller.persistence.model.LoggingConfiguration; -import org.cloudfoundry.multiapps.controller.persistence.services.cloudlogging.CloudLoggingServiceConfigurationService; import org.cloudfoundry.multiapps.controller.persistence.services.DescriptorBackupService; import org.cloudfoundry.multiapps.controller.persistence.services.FileService; import org.cloudfoundry.multiapps.controller.persistence.services.FileStorageException; import org.cloudfoundry.multiapps.controller.persistence.services.HistoricOperationEventService; -import org.cloudfoundry.multiapps.controller.core.cloudlogging.CloudLoggingServiceClient; import org.cloudfoundry.multiapps.controller.persistence.services.OperationService; +import org.cloudfoundry.multiapps.controller.persistence.services.cloudlogging.CloudLoggingServiceConfigurationService; import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.dynatrace.DynatraceProcessDuration; import org.cloudfoundry.multiapps.controller.process.dynatrace.DynatracePublisher; @@ -213,7 +213,9 @@ private void deleteCloudLoggingServiceConfiguration(DelegateExecution execution) ProcessType processType = processTypeParser.getProcessType(execution); if (processType.equals(ProcessType.UNDEPLOY)) { - cloudLoggingServiceConfigurationService.createQuery().id(loggingConfiguration.getId()).delete(); + cloudLoggingServiceConfigurationService.createQuery() + .id(loggingConfiguration.getId()) + .delete(); cloudLoggingServiceConfigurationAuditLog.logDeleteLoggingConfiguration(VariableHandling.get(execution, Variables.USER), VariableHandling.get(execution, Variables.SPACE_GUID), loggingConfiguration); diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClientTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClientTest.java index 27fd087ce1..8f9d46421c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClientTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClientTest.java @@ -4,7 +4,7 @@ import java.util.Optional; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.ApplicationServicesUpdateCallback; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateServiceStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateServiceStepTest.java index 51fe4a72c7..cfa0657540 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateServiceStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CreateServiceStepTest.java @@ -6,13 +6,13 @@ import java.util.UUID; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DeleteServiceMtaMetadataStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DeleteServiceMtaMetadataStepTest.java index 96a150b2fc..650b4faa3c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DeleteServiceMtaMetadataStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DeleteServiceMtaMetadataStepTest.java @@ -2,7 +2,7 @@ import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServiceKeysFromMtaStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServiceKeysFromMtaStepTest.java index 6dc70e3ff9..b3f581c732 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServiceKeysFromMtaStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServiceKeysFromMtaStepTest.java @@ -5,7 +5,7 @@ import java.util.UUID; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServicesFromMtaStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServicesFromMtaStepTest.java index 40b15d83d3..d3b8e2005e 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServicesFromMtaStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetachServicesFromMtaStepTest.java @@ -6,7 +6,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetectDeployedMtaStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetectDeployedMtaStepTest.java index 9d9c09c8db..418341ae0d 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetectDeployedMtaStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetectDeployedMtaStepTest.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Optional; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.test.Tester.Expectation; import org.cloudfoundry.multiapps.common.util.JsonUtil; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStepTest.java index 069619f4e8..62a2cad73e 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceCreateUpdateServiceActionsStepTest.java @@ -8,13 +8,13 @@ import java.util.UUID; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceOperation; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceDeleteActionsToExecuteStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceDeleteActionsToExecuteStepTest.java index f04e7ca7b6..41a47a3cd8 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceDeleteActionsToExecuteStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceDeleteActionsToExecuteStepTest.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStepTest.java index 82da244ea5..be639d0bd6 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineServiceKeysToRecreateStepTest.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ExtractBatchedServicesWithResolvedDynamicParametersStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ExtractBatchedServicesWithResolvedDynamicParametersStepTest.java index 1a1384edb2..4f8d239545 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ExtractBatchedServicesWithResolvedDynamicParametersStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/ExtractBatchedServicesWithResolvedDynamicParametersStepTest.java @@ -9,8 +9,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; import org.cloudfoundry.multiapps.common.ContentException; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.core.cf.v2.ResourceType; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingOperationExecutionTest.java index c4c30ded04..c4304b6b11 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingOperationExecutionTest.java @@ -3,10 +3,10 @@ import java.util.List; import java.util.UUID; -import org.cloudfoundry.client.v3.jobs.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.junit.jupiter.api.Test; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokerOperationsExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokerOperationsExecutionTest.java index fb82e84353..cafb3e24bd 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokerOperationsExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokerOperationsExecutionTest.java @@ -3,8 +3,8 @@ import java.util.List; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.jobs.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceBroker; import org.cloudfoundry.multiapps.controller.process.variables.Variables; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokersOperationsExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokersOperationsExecutionTest.java index 94760131fd..c00b848696 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokersOperationsExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBrokersOperationsExecutionTest.java @@ -5,8 +5,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.jobs.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceKeyCreationOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceKeyCreationOperationExecutionTest.java index a3b563ae2c..0836b1c072 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceKeyCreationOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceKeyCreationOperationExecutionTest.java @@ -4,8 +4,8 @@ import java.util.UUID; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.jobs.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java index 671c01c888..e1ad3e6065 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java @@ -2,13 +2,13 @@ import java.util.List; import java.util.UUID; -import org.cloudfoundry.client.v3.jobs.JobState; -import org.cloudfoundry.client.v3.serviceinstances.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceInstance; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudAsyncJob; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudServiceInstance; +import org.cloudfoundry.multiapps.controller.client.facade.domain.JobState; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceInstanceType; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudApplicationExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStepTest.java index ee1671799e..c3c511a19d 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareApplicationForBackupStepTest.java @@ -3,7 +3,7 @@ import java.util.UUID; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudApplicationExtended; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareBackupMtaForDeploymentStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareBackupMtaForDeploymentStepTest.java index 861a639057..4d9262cec4 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareBackupMtaForDeploymentStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PrepareBackupMtaForDeploymentStepTest.java @@ -13,7 +13,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.controller.core.cf.detect.DeployedMtaDetector; import org.cloudfoundry.multiapps.controller.core.cf.metadata.ImmutableMtaMetadata; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStepTest.java index 3d67e6d51f..a3e0442a32 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/RemoveMtaBackupMetadataStepTest.java @@ -2,7 +2,7 @@ import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; import org.cloudfoundry.multiapps.controller.core.cf.metadata.ImmutableMtaMetadata; import org.cloudfoundry.multiapps.controller.core.cf.metadata.MtaMetadataAnnotations; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/StepsTestUtil.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/StepsTestUtil.java index 53e1e33fb0..daf246a99c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/StepsTestUtil.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/StepsTestUtil.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStepTest.java index 59ceec125e..5a3065be72 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/SyncFlowableStepTest.java @@ -8,7 +8,7 @@ import java.util.UUID; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.common.test.Tester; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudMetadata; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UpdateServiceKeysMetadataStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UpdateServiceKeysMetadataStepTest.java index 1533a3af32..7f9d40033c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UpdateServiceKeysMetadataStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UpdateServiceKeysMetadataStepTest.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.UUID; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudMetadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceKey; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ExistingAppsToBackupCalculatorTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ExistingAppsToBackupCalculatorTest.java index 1147c87e67..a4d2c66be7 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ExistingAppsToBackupCalculatorTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ExistingAppsToBackupCalculatorTest.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudApplication; import org.cloudfoundry.multiapps.controller.core.cf.metadata.ImmutableMtaMetadata; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ModuleDeterminerTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ModuleDeterminerTest.java index 3b9cb47816..dfa90198c3 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ModuleDeterminerTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ModuleDeterminerTest.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.Map; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudApplicationExtended; import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientProvider; diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandlerTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandlerTest.java index de3062a4e9..fc6f8aab18 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandlerTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/OperationInFinalStateHandlerTest.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.stream.Stream; -import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.multiapps.controller.client.facade.domain.Metadata; import org.cloudfoundry.multiapps.controller.api.model.ImmutableOperation; import org.cloudfoundry.multiapps.controller.api.model.Operation; import org.cloudfoundry.multiapps.controller.api.model.Operation.State; diff --git a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/upload/UploadFromUrlContext.java b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/upload/UploadFromUrlContext.java index c1ef04215d..18134dd785 100644 --- a/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/upload/UploadFromUrlContext.java +++ b/multiapps-controller-web/src/main/java/org/cloudfoundry/multiapps/controller/web/upload/UploadFromUrlContext.java @@ -4,7 +4,7 @@ import java.util.concurrent.atomic.AtomicLong; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.cloudfoundry.Nullable; +import org.cloudfoundry.multiapps.controller.client.facade.Nullable; import org.cloudfoundry.multiapps.controller.api.model.UserCredentials; import org.cloudfoundry.multiapps.controller.persistence.model.AsyncUploadJobEntry; import org.immutables.value.Value; diff --git a/pom.xml b/pom.xml index 87d54ee76e..8d82fefe5b 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,6 @@ 2.22.2 4.33.0 5.1.0 - 5.16.0.RELEASE 1.3.7 4.2.17.Final 1.6.16 @@ -59,12 +58,12 @@ 1.28.0 3.3.2 1.3.6 + 0.12.6 2.70.0 0.134.0 12.35.0 1.0.4 4.0.1 - 6.3.0 multiapps-controller-client @@ -825,13 +824,6 @@ resilience4j-ratelimiter ${resilience4j.version} - - - - com.squareup.wire - wire-runtime - ${wire-runtime.version} - org.bouncycastle @@ -850,12 +842,6 @@ java-cfenv-jdbc ${java-cfenv.version} - - - org.cloudfoundry - cloudfoundry-client-reactor - ${cloudfoundry-client.version} -