Skip to content

Commit ee2531e

Browse files
KehrlannLokowandtg
andauthored
Use v3 version when v2 unavailable (#1359)
* make compatibilityChecker robust if v2 api is disabled Co-authored-by: Georg Lokowandt <georg.lokowandt@sap.com>
1 parent 9426f1e commit ee2531e

13 files changed

Lines changed: 470 additions & 54 deletions

File tree

cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/CloudFoundryClientCompatibilityChecker.java

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,30 @@
2020

2121
import com.github.zafarkhaja.semver.Version;
2222
import org.cloudfoundry.client.CloudFoundryClient;
23+
import org.cloudfoundry.client.GetRootRequest;
24+
import org.cloudfoundry.client.Root;
2325
import org.cloudfoundry.client.v2.info.GetInfoRequest;
2426
import org.cloudfoundry.client.v2.info.Info;
2527
import org.slf4j.Logger;
2628
import org.slf4j.LoggerFactory;
2729
import reactor.core.publisher.Mono;
30+
import reactor.util.function.Tuple2;
2831

2932
final class CloudFoundryClientCompatibilityChecker {
3033

3134
private final Logger logger = LoggerFactory.getLogger("cloudfoundry-client.compatibility");
3235

3336
private final Info info;
37+
private final Root root;
3438

35-
CloudFoundryClientCompatibilityChecker(Info info) {
39+
CloudFoundryClientCompatibilityChecker(Info info, Root root) {
3640
this.info = info;
41+
this.root = root;
3742
}
3843

3944
void check() {
40-
this.info
41-
.get(GetInfoRequest.builder().build())
42-
.map(response -> Version.valueOf(response.getApiVersion()))
43-
.zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION)))
45+
Mono<Tuple2<Version, Version>> v2 = checkV2();
46+
v2.switchIfEmpty(checkV3())
4447
.subscribe(
4548
consumer(
4649
(server, supported) ->
@@ -51,11 +54,41 @@ void check() {
5154
t));
5255
}
5356

57+
private Mono<Tuple2<Version, Version>> checkV2() {
58+
return this.info
59+
.get(GetInfoRequest.builder().build())
60+
.flatMap(
61+
response -> {
62+
String version = response.getApiVersion();
63+
if (version == null || version.isEmpty()) {
64+
if ("CF API v2 is disabled".equals(response.getSupport())) {
65+
this.logger.warn(
66+
"calling v2 info endpoint but CF API v2 is disabled",
67+
response);
68+
}
69+
return Mono.empty();
70+
} else {
71+
return Mono.just(Version.valueOf(version));
72+
}
73+
})
74+
.zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION)));
75+
}
76+
77+
private Mono<Tuple2<Version, Version>> checkV3() {
78+
return this.root
79+
.get(GetRootRequest.builder().build())
80+
.map(
81+
response -> {
82+
String versionV3 = response.getApiVersionV3();
83+
return Version.valueOf(versionV3);
84+
})
85+
.zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION_V3)));
86+
}
87+
5488
private static void logCompatibility(Version server, Version supported, Logger logger) {
5589
String message =
5690
"Client supports API version {} and is connected to server with API version {}."
5791
+ " Things may not work as expected.";
58-
5992
if (server.greaterThan(supported)) {
6093
logger.info(message, supported, server);
6194
} else if (server.lessThan(supported)) {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2013-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.cloudfoundry.reactor.client;
18+
19+
import java.util.Map;
20+
import org.cloudfoundry.client.GetRootRequest;
21+
import org.cloudfoundry.client.GetRootResponse;
22+
import org.cloudfoundry.client.Root;
23+
import org.cloudfoundry.reactor.ConnectionContext;
24+
import org.cloudfoundry.reactor.TokenProvider;
25+
import org.cloudfoundry.reactor.client.v3.AbstractClientV3Operations;
26+
import reactor.core.publisher.Mono;
27+
28+
/**
29+
* The Reactor-based implementation of {@link Root}
30+
*/
31+
public class ReactorRoot extends AbstractClientV3Operations implements Root {
32+
33+
/**
34+
* Creates an instance
35+
*
36+
* @param connectionContext the {@link ConnectionContext} to use when communicating with the server
37+
* @param root the root URI of the server. Typically something like {@code https://api.run.pivotal.io}.
38+
* @param tokenProvider the {@link TokenProvider} to use when communicating with the server
39+
* @param requestTags map with custom http headers which will be added to web request
40+
*/
41+
public ReactorRoot(
42+
ConnectionContext connectionContext,
43+
Mono<String> root,
44+
TokenProvider tokenProvider,
45+
Map<String, String> requestTags) {
46+
super(connectionContext, root, tokenProvider, requestTags);
47+
}
48+
49+
@Override
50+
public Mono<GetRootResponse> get(GetRootRequest request) {
51+
return get(request, GetRootResponse.class, builder -> builder.pathSegment("")).checkpoint();
52+
}
53+
}

cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/client/_ReactorCloudFoundryClient.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import jakarta.annotation.PostConstruct;
2020
import org.cloudfoundry.client.CloudFoundryClient;
21+
import org.cloudfoundry.client.Root;
2122
import org.cloudfoundry.client.v2.applications.ApplicationsV2;
2223
import org.cloudfoundry.client.v2.applicationusageevents.ApplicationUsageEvents;
2324
import org.cloudfoundry.client.v2.blobstores.Blobstores;
@@ -205,7 +206,7 @@ public Builds builds() {
205206

206207
@PostConstruct
207208
public void checkCompatibility() {
208-
new CloudFoundryClientCompatibilityChecker(info()).check();
209+
new CloudFoundryClientCompatibilityChecker(info(), rootEndpoint()).check();
209210
}
210211

211212
@Override
@@ -257,6 +258,13 @@ public Info info() {
257258
return new ReactorInfo(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
258259
}
259260

261+
@Override
262+
@Value.Derived
263+
public Root rootEndpoint() {
264+
Mono<String> root = getConnectionContext().getRootProvider().getRoot(getConnectionContext());
265+
return new ReactorRoot(getConnectionContext(), root, getTokenProvider(), getRequestTags());
266+
}
267+
260268
@Override
261269
@Value.Derived
262270
public IsolationSegments isolationSegments() {

cloudfoundry-client/src/main/java/org/cloudfoundry/client/CloudFoundryClient.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ public interface CloudFoundryClient {
8585
*/
8686
String SUPPORTED_API_VERSION = "2.272.0";
8787

88+
String SUPPORTED_API_VERSION_V3 = "3.216.0";
89+
8890
/**
8991
* Main entry point to the Cloud Foundry Application Usage Events Client API
9092
*/
@@ -170,6 +172,11 @@ public interface CloudFoundryClient {
170172
*/
171173
Info info();
172174

175+
/**
176+
* Main entry point to the Cloud Foundry root endpoint
177+
*/
178+
Root rootEndpoint();
179+
173180
/**
174181
* Main entry point to the Cloud Foundry Isolation Segments API
175182
*/
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2013-2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.cloudfoundry.client;
18+
19+
import reactor.core.publisher.Mono;
20+
21+
/**
22+
* Main entry point to the Cloud Foundry RootV3 Client API
23+
*/
24+
public interface Root {
25+
26+
/**
27+
* Makes the <a href="https://v3-apidocs.cloudfoundry.org/index.html#root">Get root</a> request
28+
*
29+
* @param request the Get Root request
30+
* @return the response from the Get Root request
31+
*/
32+
Mono<GetRootResponse> get(GetRootRequest request);
33+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2013-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.cloudfoundry.client;
18+
19+
import org.immutables.value.Value;
20+
21+
/**
22+
* The request payload for the Get root "/" operation
23+
*/
24+
@Value.Immutable
25+
abstract class _GetRootRequest {
26+
27+
}

0 commit comments

Comments
 (0)