From eb15b0638dbf38818b7351a44167d75e7ea4ce53 Mon Sep 17 00:00:00 2001 From: Georg Lokowandt Date: Tue, 28 Apr 2026 14:12:07 +0200 Subject: [PATCH 1/2] make compatibilityChecker robust if v2 api is disabled --- .../CloudFoundryClientCompatibilityChecker.java | 14 +++++++++++++- .../IntegrationTestConfiguration.java | 15 ++++++++++++++- .../java/org/cloudfoundry/client/v2/InfoTest.java | 8 +++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java index e2aa39459b..df549f7f28 100644 --- a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java +++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java @@ -39,7 +39,19 @@ final class CloudFoundryClientCompatibilityChecker { void check() { this.info .get(GetInfoRequest.builder().build()) - .map(response -> Version.valueOf(response.getApiVersion())) + .map( + response -> { + String version = response.getApiVersion(); + if (version == null || version.isEmpty()) { + if ("CF API v2 is disabled".equals(response.getSupport())) { + this.logger.warn( + "calling v2 info endpoint but CF API v2 is disabled", + response); + } + return Version.of(0, 0, 0); + } + return Version.valueOf(version); + }) .zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION))) .subscribe( consumer( diff --git a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java index 58b01252f0..5fa475c00d 100644 --- a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java +++ b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java @@ -468,7 +468,20 @@ Version serverVersion(@Qualifier("admin") CloudFoundryClient cloudFoundryClient) return cloudFoundryClient .info() .get(GetInfoRequest.builder().build()) - .map(response -> Version.valueOf(response.getApiVersion())) + .map( + response -> { + String version = response.getApiVersion(); + if (version == null || version.isEmpty()) { + this.logger.warn( + "calling v2 info endpoint but CF API v2 is disabled"); + return new Version.Builder() + .setMajorVersion(0) + .setMinorVersion(0) + .setPatchVersion(0) + .build(); + } + return Version.valueOf(version); + }) .doOnSubscribe(s -> this.logger.debug(">> CLOUD FOUNDRY VERSION <<")) .doOnSuccess(r -> this.logger.debug("<< CLOUD FOUNDRY VERSION >>")) .block(); diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java index 471cb284e8..9c1841c687 100644 --- a/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java +++ b/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java @@ -41,7 +41,13 @@ public void info() { .consumeNextWith( response -> { Version expected = Version.valueOf(SUPPORTED_API_VERSION); - Version actual = Version.valueOf(response.getApiVersion()); + Version actual; + String version = response.getApiVersion(); + if (version == null || version.isEmpty()) { + actual = Version.of(0, 0, 0); + } else { + actual = Version.valueOf(version); + } assertThat(actual).isLessThanOrEqualTo(expected); }) From cf714d22100f643148b5de5c64b05b4a9e6aa6b4 Mon Sep 17 00:00:00 2001 From: Georg Lokowandt Date: Tue, 5 May 2026 23:44:11 +0200 Subject: [PATCH 2/2] make CompatibilityChecker robust if v2 api is disabled. fix for #1229 --- ...loudFoundryClientCompatibilityChecker.java | 51 ++-- .../reactor/client/ReactorRoot.java | 53 +++++ .../client/_ReactorCloudFoundryClient.java | 10 +- .../client/CloudFoundryClient.java | 7 + .../java/org/cloudfoundry/client/Root.java | 33 +++ .../cloudfoundry/client/_GetRootRequest.java | 27 +++ .../cloudfoundry/client/_GetRootResponse.java | 218 ++++++++++++++++++ .../IntegrationTestConfiguration.java | 33 ++- .../org/cloudfoundry/client/v2/InfoTest.java | 2 + 9 files changed, 407 insertions(+), 27 deletions(-) create mode 100644 cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/ReactorRoot.java create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/Root.java create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootRequest.java create mode 100644 cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootResponse.java diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java index df549f7f28..d540e1f70c 100644 --- a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java +++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java @@ -20,26 +20,44 @@ import com.github.zafarkhaja.semver.Version; import org.cloudfoundry.client.CloudFoundryClient; +import org.cloudfoundry.client.GetRootRequest; +import org.cloudfoundry.client.Root; import org.cloudfoundry.client.v2.info.GetInfoRequest; import org.cloudfoundry.client.v2.info.Info; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; +import reactor.util.function.Tuple2; final class CloudFoundryClientCompatibilityChecker { private final Logger logger = LoggerFactory.getLogger("cloudfoundry-client.compatibility"); private final Info info; + private final Root root; - CloudFoundryClientCompatibilityChecker(Info info) { + CloudFoundryClientCompatibilityChecker(Info info, Root root) { this.info = info; + this.root = root; } void check() { - this.info + Mono> v2 = checkV2(); + v2.switchIfEmpty(checkV3()) + .subscribe( + consumer( + (server, supported) -> + logCompatibility(server, supported, this.logger)), + t -> + this.logger.error( + "An error occurred while checking version compatibility:", + t)); + } + + private Mono> checkV2() { + return this.info .get(GetInfoRequest.builder().build()) - .map( + .flatMap( response -> { String version = response.getApiVersion(); if (version == null || version.isEmpty()) { @@ -48,26 +66,29 @@ void check() { "calling v2 info endpoint but CF API v2 is disabled", response); } - return Version.of(0, 0, 0); + return Mono.empty(); + } else { + return Mono.just(Version.valueOf(version)); } - return Version.valueOf(version); }) - .zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION))) - .subscribe( - consumer( - (server, supported) -> - logCompatibility(server, supported, this.logger)), - t -> - this.logger.error( - "An error occurred while checking version compatibility:", - t)); + .zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION))); + } + + private Mono> checkV3() { + return this.root + .get(GetRootRequest.builder().build()) + .map( + response -> { + String versionV3 = response.getApiVersionV3(); + return Version.valueOf(versionV3); + }) + .zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION_V3))); } private static void logCompatibility(Version server, Version supported, Logger logger) { String message = "Client supports API version {} and is connected to server with API version {}." + " Things may not work as expected."; - if (server.greaterThan(supported)) { logger.info(message, supported, server); } else if (server.lessThan(supported)) { diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/ReactorRoot.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/ReactorRoot.java new file mode 100644 index 0000000000..7cf690307a --- /dev/null +++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/ReactorRoot.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.cloudfoundry.reactor.client; + +import java.util.Map; +import org.cloudfoundry.client.GetRootRequest; +import org.cloudfoundry.client.GetRootResponse; +import org.cloudfoundry.client.Root; +import org.cloudfoundry.reactor.ConnectionContext; +import org.cloudfoundry.reactor.TokenProvider; +import org.cloudfoundry.reactor.client.v3.AbstractClientV3Operations; +import reactor.core.publisher.Mono; + +/** + * The Reactor-based implementation of {@link Root} + */ +public class ReactorRoot extends AbstractClientV3Operations implements Root { + + /** + * Creates an instance + * + * @param connectionContext the {@link ConnectionContext} to use when communicating with the server + * @param root the root URI of the server. Typically something like {@code https://api.run.pivotal.io}. + * @param tokenProvider the {@link TokenProvider} to use when communicating with the server + * @param requestTags map with custom http headers which will be added to web request + */ + public ReactorRoot( + ConnectionContext connectionContext, + Mono root, + TokenProvider tokenProvider, + Map requestTags) { + super(connectionContext, root, tokenProvider, requestTags); + } + + @Override + public Mono get(GetRootRequest request) { + return get(request, GetRootResponse.class, builder -> builder.pathSegment("")).checkpoint(); + } +} diff --git a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java index 80e5ef2614..7a26cd13d2 100644 --- a/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java +++ b/cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java @@ -18,6 +18,7 @@ import jakarta.annotation.PostConstruct; import org.cloudfoundry.client.CloudFoundryClient; +import org.cloudfoundry.client.Root; import org.cloudfoundry.client.v2.applications.ApplicationsV2; import org.cloudfoundry.client.v2.applicationusageevents.ApplicationUsageEvents; import org.cloudfoundry.client.v2.blobstores.Blobstores; @@ -207,7 +208,7 @@ public Builds builds() { @PostConstruct public void checkCompatibility() { - new CloudFoundryClientCompatibilityChecker(info()).check(); + new CloudFoundryClientCompatibilityChecker(info(), rootEndpoint()).check(); } @Override @@ -259,6 +260,13 @@ public Info info() { return new ReactorInfo(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags()); } + @Override + @Value.Derived + public Root rootEndpoint() { + Mono root = getConnectionContext().getRootProvider().getRoot(getConnectionContext()); + return new ReactorRoot(getConnectionContext(), root, getTokenProvider(), getRequestTags()); + } + @Override @Value.Derived public IsolationSegments isolationSegments() { diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java index 0f85e973e5..6181dfff7a 100644 --- a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java @@ -86,6 +86,8 @@ public interface CloudFoundryClient { */ String SUPPORTED_API_VERSION = "2.272.0"; + String SUPPORTED_API_VERSION_V3 = "3.216.0"; + /** * Main entry point to the Cloud Foundry Application Usage Events Client API */ @@ -171,6 +173,11 @@ public interface CloudFoundryClient { */ Info info(); + /** + * Main entry point to the Cloud Foundry root endpoint + */ + Root rootEndpoint(); + /** * Main entry point to the Cloud Foundry Isolation Segments API */ diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/Root.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/Root.java new file mode 100644 index 0000000000..37c59a4d7e --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/Root.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2026 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.cloudfoundry.client; + +import reactor.core.publisher.Mono; + +/** + * Main entry point to the Cloud Foundry RootV3 Client API + */ +public interface Root { + + /** + * Makes the Get root request + * + * @param request the Get Root request + * @return the response from the Get Root request + */ + Mono get(GetRootRequest request); +} diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootRequest.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootRequest.java new file mode 100644 index 0000000000..a0aab3e5f1 --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.cloudfoundry.client; + +import org.immutables.value.Value; + +/** + * The request payload for the Get root "/" operation + */ +@Value.Immutable +abstract class _GetRootRequest { + +} diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootResponse.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootResponse.java new file mode 100644 index 0000000000..f56a890a5c --- /dev/null +++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/client/_GetRootResponse.java @@ -0,0 +1,218 @@ +/* + * Copyright 2013-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.cloudfoundry.client; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +import org.cloudfoundry.Nullable; +import org.immutables.value.Value; + +/** + * The response payload for the get root operation. See V3 API Root + */ +@JsonDeserialize(using = org.cloudfoundry.client._GetRootResponse.RootDeserializer.class) +@Value.Immutable() +public interface _GetRootResponse { + + /** + * The root of the cloud controller Api version 3 + */ + @Value.Parameter + @Nullable + public abstract String getApiV3(); + + /** + * The version of the cloud controller Api version 3 + */ + @Value.Parameter + @Nullable + public abstract String getApiVersionV3(); + + /** + * The root of the cloud controller Api version 2 + */ + @Value.Parameter + @Nullable + public abstract String getApiV2(); + + /** + * The version of the cloud controller Api version 2 (if available) + */ + @Value.Parameter + @Nullable + public abstract String getApiVersion(); + + /** + * The network policy v0 endpoint + */ + @Value.Parameter + @Nullable + public abstract String getNetworkPolicyV0Endpoint(); + + /** + * The network policy v1 endpoint + */ + @Value.Parameter + @Nullable + public abstract String getNetworkPolicyV1Endpoint(); + + /** + * The login endpoint + */ + @Value.Parameter + @Nullable + public abstract String getLoginEndpoint(); + + /** + * The uaa endpoint + */ + @Value.Parameter + @Nullable + public abstract String getUaaEndpoint(); + + /** + * The credhub endpoint + */ + @Value.Parameter + @Nullable + public abstract String getCredhubEndpoint(); + + /** + * The routing endpoint + */ + @Value.Parameter + @Nullable + public abstract String getRoutingEndpoint(); + + /** + * The loggin encpoint + */ + @Value.Parameter + @Nullable + public abstract String getLoggingEndpoint(); + + /** + * The log cache url + */ + @Value.Parameter + @Nullable + public abstract String getLogCacheEndpoint(); + + /** + * The log stream url + */ + @Value.Parameter + @Nullable + public abstract String getLogStreamEndpoint(); + + /** + * The ssh endpoint for apps. + */ + @Value.Parameter + @Nullable + public abstract String getAppSshEndpoint(); + + /** + * The ssh host key fingerprint for apps. + */ + @Value.Parameter + @Nullable + public abstract String getAppSshHostKeyFingerprint(); + + /** + * The ssh oauth client for apps. + */ + @Value.Parameter + @Nullable + public abstract String getAppSshOauthClient(); + + /** + * The self url + */ + @Value.Parameter + @Nullable + public abstract String getSelf(); + + public class RootDeserializer extends StdDeserializer<_GetRootResponse>{ + private static final long serialVersionUID = 1L; + + protected RootDeserializer() { + super(GetRootResponse.class); + } + @Override + public _GetRootResponse deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JacksonException { + JsonNode productNode = jp.getCodec().readTree(jp); + String apiV3Endpoint = getEndpoint("cloud_controller_v3",productNode); + + JsonNode tmp = productNode.get("links").get("cloud_controller_v3"); + String apiVersionV3 = null; + if(tmp!=null) { + apiVersionV3 = tmp.get("meta").get("version").textValue(); + } + + String apiV2Endpoint = getEndpoint("cloud_controller_v2",productNode); + tmp = productNode.get("links").get("cloud_controller_v2"); + String apiVersion = null; + if(tmp!=null) { + apiVersion = tmp.get("meta").get("version").textValue(); + } + String networkPolicyV0Endpoint = getEndpoint("network_policy_v0",productNode); + String networkPolicyV1Endpoint = getEndpoint("network_policy_v1",productNode); + String loginEndpoint = getEndpoint("login",productNode); + String uaaEndpoint = getEndpoint("uaa",productNode); + String credhubEndpoint = getEndpoint("credhub",productNode); + String routingEndpoint = getEndpoint("routing",productNode); + String loggingEndpoint = getEndpoint("logging",productNode); + String logCacheEndpoint = getEndpoint("log_cache",productNode); + String logStreamEndpoint = getEndpoint("log_stream",productNode); + String appSshEndpoint = getEndpoint("app_ssh",productNode); + tmp = productNode.get("links").get("app_ssh"); + String appSshHostKeyFingerprint = null; + if(tmp!=null) { + appSshHostKeyFingerprint = tmp.get("meta").get("host_key_fingerprint").textValue(); + } + tmp = productNode.get("links").get("app_ssh"); + String appSshOauthClient = null; + if(tmp!=null) { + appSshOauthClient = tmp.get("meta").get("oauth_client").textValue(); + } + + String self = getEndpoint("self",productNode); + return GetRootResponse.of(apiV3Endpoint,apiVersionV3,apiV2Endpoint, + apiVersion,networkPolicyV0Endpoint, networkPolicyV1Endpoint, loginEndpoint,uaaEndpoint,credhubEndpoint, + routingEndpoint,loggingEndpoint, logCacheEndpoint, logStreamEndpoint,appSshEndpoint, appSshHostKeyFingerprint, appSshOauthClient,self); + } + + // null safe access to href-endpoints + private String getEndpoint(String name,JsonNode productNode) { + String result = null; + JsonNode tmp = productNode.get("links").get(name); + if(tmp!=null&& !tmp.isNull()) { + result = tmp.get("href").textValue(); + } + return result; + } + } +} diff --git a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java index 5fa475c00d..603c468207 100644 --- a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java +++ b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java @@ -38,6 +38,7 @@ import java.util.HashMap; import java.util.List; import org.cloudfoundry.client.CloudFoundryClient; +import org.cloudfoundry.client.GetRootRequest; import org.cloudfoundry.client.v2.info.GetInfoRequest; import org.cloudfoundry.client.v2.organizationquotadefinitions.CreateOrganizationQuotaDefinitionRequest; import org.cloudfoundry.client.v2.organizations.AssociateOrganizationManagerRequest; @@ -465,26 +466,36 @@ RoutingClient routingClient(ConnectionContext connectionContext, TokenProvider t @Bean Version serverVersion(@Qualifier("admin") CloudFoundryClient cloudFoundryClient) { + return serverVersionV2(cloudFoundryClient) + .switchIfEmpty(serverVersionV3(cloudFoundryClient)) + .doOnSubscribe(s -> this.logger.debug(">> CLOUD FOUNDRY VERSION <<")) + .doOnSuccess(r -> this.logger.debug("<< CLOUD FOUNDRY VERSION >>")) + .block(); + } + + private Mono serverVersionV2( + @Qualifier("admin") CloudFoundryClient cloudFoundryClient) { return cloudFoundryClient .info() .get(GetInfoRequest.builder().build()) - .map( + .flatMap( response -> { String version = response.getApiVersion(); if (version == null || version.isEmpty()) { this.logger.warn( "calling v2 info endpoint but CF API v2 is disabled"); - return new Version.Builder() - .setMajorVersion(0) - .setMinorVersion(0) - .setPatchVersion(0) - .build(); + return Mono.empty(); } - return Version.valueOf(version); - }) - .doOnSubscribe(s -> this.logger.debug(">> CLOUD FOUNDRY VERSION <<")) - .doOnSuccess(r -> this.logger.debug("<< CLOUD FOUNDRY VERSION >>")) - .block(); + return Mono.just(Version.valueOf(version)); + }); + } + + private Mono serverVersionV3( + @Qualifier("admin") CloudFoundryClient cloudFoundryClient) { + return cloudFoundryClient + .rootEndpoint() + .get(GetRootRequest.builder().build()) + .map(response -> Version.valueOf(response.getApiVersionV3())); } @Lazy diff --git a/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java b/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java index 9c1841c687..f12d833f2e 100644 --- a/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java +++ b/integration-test/src/test/java/org/cloudfoundry/client/v2/InfoTest.java @@ -44,6 +44,8 @@ public void info() { Version actual; String version = response.getApiVersion(); if (version == null || version.isEmpty()) { + assertThat("CF API v2 is disabled") + .isEqualTo(response.getSupport()); actual = Version.of(0, 0, 0); } else { actual = Version.valueOf(version);