Skip to content

Commit cf714d2

Browse files
committed
make CompatibilityChecker robust if v2 api is disabled. fix for #1229
1 parent eb15b06 commit cf714d2

9 files changed

Lines changed: 407 additions & 27 deletions

File tree

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

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,44 @@
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
45+
Mono<Tuple2<Version, Version>> v2 = checkV2();
46+
v2.switchIfEmpty(checkV3())
47+
.subscribe(
48+
consumer(
49+
(server, supported) ->
50+
logCompatibility(server, supported, this.logger)),
51+
t ->
52+
this.logger.error(
53+
"An error occurred while checking version compatibility:",
54+
t));
55+
}
56+
57+
private Mono<Tuple2<Version, Version>> checkV2() {
58+
return this.info
4159
.get(GetInfoRequest.builder().build())
42-
.map(
60+
.flatMap(
4361
response -> {
4462
String version = response.getApiVersion();
4563
if (version == null || version.isEmpty()) {
@@ -48,26 +66,29 @@ void check() {
4866
"calling v2 info endpoint but CF API v2 is disabled",
4967
response);
5068
}
51-
return Version.of(0, 0, 0);
69+
return Mono.empty();
70+
} else {
71+
return Mono.just(Version.valueOf(version));
5272
}
53-
return Version.valueOf(version);
5473
})
55-
.zipWith(Mono.just(Version.valueOf(CloudFoundryClient.SUPPORTED_API_VERSION)))
56-
.subscribe(
57-
consumer(
58-
(server, supported) ->
59-
logCompatibility(server, supported, this.logger)),
60-
t ->
61-
this.logger.error(
62-
"An error occurred while checking version compatibility:",
63-
t));
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)));
6486
}
6587

6688
private static void logCompatibility(Version server, Version supported, Logger logger) {
6789
String message =
6890
"Client supports API version {} and is connected to server with API version {}."
6991
+ " Things may not work as expected.";
70-
7192
if (server.greaterThan(supported)) {
7293
logger.info(message, supported, server);
7394
} 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;
@@ -207,7 +208,7 @@ public Builds builds() {
207208

208209
@PostConstruct
209210
public void checkCompatibility() {
210-
new CloudFoundryClientCompatibilityChecker(info()).check();
211+
new CloudFoundryClientCompatibilityChecker(info(), rootEndpoint()).check();
211212
}
212213

213214
@Override
@@ -259,6 +260,13 @@ public Info info() {
259260
return new ReactorInfo(getConnectionContext(), getRootV2(), getTokenProvider(), getRequestTags());
260261
}
261262

263+
@Override
264+
@Value.Derived
265+
public Root rootEndpoint() {
266+
Mono<String> root = getConnectionContext().getRootProvider().getRoot(getConnectionContext());
267+
return new ReactorRoot(getConnectionContext(), root, getTokenProvider(), getRequestTags());
268+
}
269+
262270
@Override
263271
@Value.Derived
264272
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
@@ -86,6 +86,8 @@ public interface CloudFoundryClient {
8686
*/
8787
String SUPPORTED_API_VERSION = "2.272.0";
8888

89+
String SUPPORTED_API_VERSION_V3 = "3.216.0";
90+
8991
/**
9092
* Main entry point to the Cloud Foundry Application Usage Events Client API
9193
*/
@@ -171,6 +173,11 @@ public interface CloudFoundryClient {
171173
*/
172174
Info info();
173175

176+
/**
177+
* Main entry point to the Cloud Foundry root endpoint
178+
*/
179+
Root rootEndpoint();
180+
174181
/**
175182
* Main entry point to the Cloud Foundry Isolation Segments API
176183
*/
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)