diff --git a/README.md b/README.md
index ffed2f36f4..058431f921 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,44 @@ The `cf-java-client` project is a Java language binding for interacting with a C
## Versions
The Cloud Foundry Java Client has two active versions. The `5.x` line is compatible with Spring Boot `2.4.x - 2.6.x` just to manage its dependencies, while the `4.x` line uses Spring Boot `2.3.x`.
+## Deprecations
+
+### `DopplerClient.recentLogs()` — Recent Logs via Doppler
+
+> [!WARNING]
+> **Deprecated since cf-java-client `5.17.x`**
+>
+> The `DopplerClient.recentLogs()` endpoint (and the related `RecentLogsRequest` / `LogMessage` types from the `org.cloudfoundry.doppler` package) are **deprecated** and will be removed in a future release.
+>
+> This API relies on the [Loggregator][loggregator] Doppler/Traffic Controller endpoint `/apps/{id}/recentlogs`, which was removed in **Loggregator ≥ 107.0**.
+> The affected platform versions are:
+>
+> | Platform | Last version with Doppler recent-logs support |
+> | -------- | --------------------------------------------- |
+> | CF Deployment (CFD) | `< 24.3` |
+> | Tanzu Application Service (TAS) | `< 4.0` |
+>
+> **Migration:** Replace any call to `DopplerClient.recentLogs()` with [`LogCacheClient.read()`][log-cache-api] (available via `org.cloudfoundry.logcache.v1.LogCacheClient`).
+>
+> ```java
+> // Before (deprecated)
+> dopplerClient.recentLogs(RecentLogsRequest.builder()
+> .applicationId(appId)
+> .build());
+>
+> // After
+> logCacheClient.read(ReadRequest.builder()
+> .sourceId(appId)
+> .envelopeTypes(EnvelopeType.LOG)
+> .build());
+> ```
+
+> [!NOTE]
+> **Operations API users:** `Applications.logs(ApplicationLogsRequest)` now uses Log Cache under the hood for recent logs (the default). No migration is needed at the Operations layer.
+
+[loggregator]: https://github.com/cloudfoundry/loggregator
+[log-cache-api]: https://github.com/cloudfoundry/log-cache
+
## Dependencies
Most projects will need two dependencies; the Operations API and an implementation of the Client API. For Maven, the dependencies would be defined like this:
@@ -76,6 +114,9 @@ Both the `cloudfoundry-operations` and `cloudfoundry-client` projects follow a [
### `CloudFoundryClient`, `DopplerClient`, `UaaClient` Builders
+> [!NOTE]
+> **`DopplerClient` — partial deprecation:** The `recentLogs()` method on `DopplerClient` is deprecated and only works against Loggregator \< 107.0 (CFD \< 24.3 / TAS \< 4.0). See the [Deprecations](#deprecations) section above for the migration path to `LogCacheClient`.
+
The lowest-level building blocks of the API are `ConnectionContext` and `TokenProvider`. These types are intended to be shared between instances of the clients, and come with out of the box implementations. To instantiate them, you configure them with builders:
```java
diff --git a/cloudfoundry-client/src/main/java/org/cloudfoundry/doppler/DopplerClient.java b/cloudfoundry-client/src/main/java/org/cloudfoundry/doppler/DopplerClient.java
index a9c03441cf..3d61922734 100644
--- a/cloudfoundry-client/src/main/java/org/cloudfoundry/doppler/DopplerClient.java
+++ b/cloudfoundry-client/src/main/java/org/cloudfoundry/doppler/DopplerClient.java
@@ -42,9 +42,15 @@ public interface DopplerClient {
/**
* Makes the Recent Logs request
*
+ * @deprecated Use {@link org.cloudfoundry.logcache.v1.LogCacheClient#read(org.cloudfoundry.logcache.v1.ReadRequest)} instead.
+ * Only works with {@code Loggregator < 107.0}, shipped in {@code CFD < 24.3} and {@code TAS < 4.0}.
* @param request the Recent Logs request
- * @return the events from the recent logs
+ * @return a flux of events from the recent logs
+ * @see Loggregator
+ * @see Log Cache
+ * @see org.cloudfoundry.logcache.v1.LogCacheClient#read(org.cloudfoundry.logcache.v1.ReadRequest)
*/
+ @Deprecated
Flux recentLogs(RecentLogsRequest request);
/**
diff --git a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java
index 299b4bf5e4..62e442a53d 100644
--- a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java
+++ b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/_DefaultCloudFoundryOperations.java
@@ -23,6 +23,7 @@
import org.cloudfoundry.client.v3.spaces.ListSpacesRequest;
import org.cloudfoundry.client.v3.spaces.SpaceResource;
import org.cloudfoundry.doppler.DopplerClient;
+import org.cloudfoundry.logcache.v1.LogCacheClient;
import org.cloudfoundry.networking.NetworkingClient;
import org.cloudfoundry.operations.advanced.Advanced;
import org.cloudfoundry.operations.advanced.DefaultAdvanced;
@@ -79,7 +80,7 @@ public Advanced advanced() {
@Override
@Value.Derived
public Applications applications() {
- return new DefaultApplications(getCloudFoundryClientPublisher(), getDopplerClientPublisher(), getSpaceId());
+ return new DefaultApplications(getCloudFoundryClientPublisher(), getDopplerClientPublisher(), getLogCacheClientPublisher(), getSpaceId());
}
@Override
@@ -185,6 +186,19 @@ Mono getDopplerClientPublisher() {
.orElse(Mono.error(new IllegalStateException("DopplerClient must be set")));
}
+ /**
+ * The {@link LogCacheClient} to use for operations functionality
+ */
+ @Nullable
+ abstract LogCacheClient getLogCacheClient();
+
+ @Value.Derived
+ Mono getLogCacheClientPublisher() {
+ return Optional.ofNullable(getLogCacheClient())
+ .map(Mono::just)
+ .orElse(Mono.error(new IllegalStateException("LogCacheClient must be set")));
+ }
+
/**
* The {@link NetworkingClient} to use for operations functionality
*/
diff --git a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/Applications.java b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/Applications.java
index 5196fef6c8..56aba6af64 100644
--- a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/Applications.java
+++ b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/Applications.java
@@ -128,8 +128,9 @@ public interface Applications {
/**
* List the applications logs.
- * Only works with {@code Loggregator < 107.0}, shipped in {@code CFD < 24.3}
- * and {@code TAS < 4.0}.
+ * Uses Log Cache under the hood when {@link ApplicationLogsRequest#getRecent()} is {@code true}.
+ * Log streaming still uses Doppler, which is not available in CF deployments following
+ * shared-nothing architecture.
*
* @param request the application logs request
* @return the applications logs
diff --git a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/DefaultApplications.java b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/DefaultApplications.java
index e51ddbb472..bb492fffe7 100644
--- a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/DefaultApplications.java
+++ b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/DefaultApplications.java
@@ -154,6 +154,9 @@
import org.cloudfoundry.doppler.LogMessage;
import org.cloudfoundry.doppler.RecentLogsRequest;
import org.cloudfoundry.doppler.StreamRequest;
+import org.cloudfoundry.logcache.v1.EnvelopeBatch;
+import org.cloudfoundry.logcache.v1.LogCacheClient;
+import org.cloudfoundry.logcache.v1.ReadRequest;
import org.cloudfoundry.operations.util.OperationsLogging;
import org.cloudfoundry.util.DateUtils;
import org.cloudfoundry.util.DelayTimeoutException;
@@ -200,6 +203,10 @@ public final class DefaultApplications implements Applications {
private static final Comparator LOG_MESSAGE_COMPARATOR =
Comparator.comparing(LogMessage::getTimestamp);
+ private static final Comparator
+ LOG_MESSAGE_COMPARATOR_LOG_CACHE =
+ Comparator.comparing(org.cloudfoundry.logcache.v1.Envelope::getTimestamp);
+
private static final Duration LOG_MESSAGE_TIMESPAN = Duration.ofMillis(500);
private static final int MAX_NUMBER_OF_RECENT_EVENTS = 50;
@@ -214,6 +221,8 @@ public final class DefaultApplications implements Applications {
private final Mono dopplerClient;
+ private final Mono logCacheClient;
+
private final RandomWords randomWords;
private final Mono spaceId;
@@ -221,17 +230,20 @@ public final class DefaultApplications implements Applications {
public DefaultApplications(
Mono cloudFoundryClient,
Mono dopplerClient,
+ Mono logCacheClient,
Mono spaceId) {
- this(cloudFoundryClient, dopplerClient, new WordListRandomWords(), spaceId);
+ this(cloudFoundryClient, dopplerClient, logCacheClient, new WordListRandomWords(), spaceId);
}
DefaultApplications(
Mono cloudFoundryClient,
Mono dopplerClient,
+ Mono logCacheClient,
RandomWords randomWords,
Mono spaceId) {
this.cloudFoundryClient = cloudFoundryClient;
this.dopplerClient = dopplerClient;
+ this.logCacheClient = logCacheClient;
this.randomWords = randomWords;
this.spaceId = spaceId;
}
@@ -529,6 +541,7 @@ public Flux listTasks(ListApplicationTasksRequest request) {
.checkpoint();
}
+ @Deprecated
@Override
public Flux logs(LogsRequest request) {
return Mono.zip(this.cloudFoundryClient, this.spaceId)
@@ -546,22 +559,23 @@ public Flux logs(LogsRequest request) {
@Override
public Flux logs(ApplicationLogsRequest request) {
- return logs(LogsRequest.builder()
- .name(request.getName())
- .recent(request.getRecent())
- .build())
- .map(
- logMessage ->
- ApplicationLog.builder()
- .sourceId(logMessage.getApplicationId())
- .sourceType(logMessage.getSourceType())
- .instanceId(logMessage.getSourceInstance())
- .message(logMessage.getMessage())
- .timestamp(logMessage.getTimestamp())
- .logType(
- ApplicationLogType.from(
- logMessage.getMessageType().name()))
- .build());
+ if (request.getRecent() == null || request.getRecent()) {
+ return Mono.zip(this.cloudFoundryClient, this.spaceId)
+ .flatMap(
+ function(
+ (cloudFoundryClient, spaceId) ->
+ getApplicationId(
+ cloudFoundryClient,
+ request.getName(),
+ spaceId)))
+ .flatMapMany(
+ applicationId -> getLogsLogCache(this.logCacheClient, applicationId))
+ .transform(OperationsLogging.log("Get Application Logs"))
+ .checkpoint();
+ } else {
+ return logs(LogsRequest.builder().name(request.getName()).recent(false).build())
+ .map(DefaultApplications::toApplicationLog);
+ }
}
@Override
@@ -673,7 +687,6 @@ public Mono pushManifestV3(PushManifestV3Request request) {
} catch (IOException e) {
throw new RuntimeException("Could not serialize manifest", e);
}
-
return Mono.zip(this.cloudFoundryClient, this.spaceId)
.flatMap(
function(
@@ -1617,6 +1630,32 @@ private static Flux getLogs(
}
}
+ private static Flux getLogsLogCache(
+ Mono logCacheClient, String applicationId) {
+ return requestLogsRecentLogCache(logCacheClient, applicationId)
+ .filter(e -> e.getLog() != null)
+ .sort(LOG_MESSAGE_COMPARATOR_LOG_CACHE)
+ .map(
+ envelope ->
+ ApplicationLog.builder()
+ .sourceId(
+ Optional.ofNullable(envelope.getSourceId())
+ .orElse(""))
+ .sourceType(
+ envelope.getTags().getOrDefault("source_type", ""))
+ .instanceId(
+ Optional.ofNullable(envelope.getInstanceId())
+ .orElse(""))
+ .message(envelope.getLog().getPayloadAsText())
+ .timestamp(
+ Optional.ofNullable(envelope.getTimestamp())
+ .orElse(0L))
+ .logType(
+ ApplicationLogType.from(
+ envelope.getLog().getType().name()))
+ .build());
+ }
+
@SuppressWarnings("unchecked")
private static Map getMetadataRequest(EventEntity entity) {
Map> metadata =
@@ -2501,6 +2540,7 @@ private static Flux requestListTasks(
.build()));
}
+ @Deprecated
private static Flux requestLogsRecent(
Mono dopplerClient, String applicationId) {
return dopplerClient.flatMapMany(
@@ -2509,6 +2549,16 @@ private static Flux requestLogsRecent(
RecentLogsRequest.builder().applicationId(applicationId).build()));
}
+ private static Flux requestLogsRecentLogCache(
+ Mono logCacheClient, String applicationId) {
+ return logCacheClient
+ .flatMap(
+ client ->
+ client.read(ReadRequest.builder().sourceId(applicationId).build()))
+ .flatMap(response -> Mono.justOrEmpty(response.getEnvelopes()))
+ .flatMapIterable(EnvelopeBatch::getBatch);
+ }
+
private static Flux requestLogsStream(
Mono dopplerClient, String applicationId) {
return dopplerClient.flatMapMany(
@@ -2914,6 +2964,17 @@ private static Mono stopApplicationIfNotStopped(
: Mono.just(resource);
}
+ private static ApplicationLog toApplicationLog(LogMessage logMessage) {
+ return ApplicationLog.builder()
+ .sourceId(logMessage.getApplicationId())
+ .sourceType(logMessage.getSourceType())
+ .instanceId(logMessage.getSourceInstance())
+ .message(logMessage.getMessage())
+ .timestamp(logMessage.getTimestamp())
+ .logType(ApplicationLogType.from(logMessage.getMessageType().name()))
+ .build();
+ }
+
private static ApplicationDetail toApplicationDetail(
List buildpacks,
SummaryApplicationResponse summaryApplicationResponse,
diff --git a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java
index d864ed497e..5a0854f48b 100644
--- a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java
+++ b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java
@@ -51,6 +51,7 @@
import org.cloudfoundry.client.v3.spaces.SpacesV3;
import org.cloudfoundry.client.v3.tasks.Tasks;
import org.cloudfoundry.doppler.DopplerClient;
+import org.cloudfoundry.logcache.v1.LogCacheClient;
import org.cloudfoundry.routing.RoutingClient;
import org.cloudfoundry.routing.v1.routergroups.RouterGroups;
import org.cloudfoundry.uaa.UaaClient;
@@ -101,6 +102,8 @@ public abstract class AbstractOperationsTest {
protected final DopplerClient dopplerClient = mock(DopplerClient.class, RETURNS_SMART_NULLS);
+ protected final LogCacheClient logCacheClient = mock(LogCacheClient.class, RETURNS_SMART_NULLS);
+
protected final Events events = mock(Events.class, RETURNS_SMART_NULLS);
protected final FeatureFlags featureFlags = mock(FeatureFlags.class, RETURNS_SMART_NULLS);
diff --git a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/applications/DefaultApplicationsTest.java b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/applications/DefaultApplicationsTest.java
index cdc9619d2d..5d21f8e584 100644
--- a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/applications/DefaultApplicationsTest.java
+++ b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/applications/DefaultApplicationsTest.java
@@ -25,9 +25,11 @@
import static org.mockito.Mockito.when;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -139,11 +141,17 @@
import org.cloudfoundry.client.v3.tasks.CreateTaskResponse;
import org.cloudfoundry.client.v3.tasks.TaskResource;
import org.cloudfoundry.doppler.DopplerClient;
-import org.cloudfoundry.doppler.Envelope;
import org.cloudfoundry.doppler.EventType;
import org.cloudfoundry.doppler.LogMessage;
import org.cloudfoundry.doppler.RecentLogsRequest;
import org.cloudfoundry.doppler.StreamRequest;
+import org.cloudfoundry.logcache.v1.Envelope;
+import org.cloudfoundry.logcache.v1.EnvelopeBatch;
+import org.cloudfoundry.logcache.v1.Log;
+import org.cloudfoundry.logcache.v1.LogCacheClient;
+import org.cloudfoundry.logcache.v1.LogType;
+import org.cloudfoundry.logcache.v1.ReadRequest;
+import org.cloudfoundry.logcache.v1.ReadResponse;
import org.cloudfoundry.operations.AbstractOperationsTest;
import org.cloudfoundry.util.DateUtils;
import org.cloudfoundry.util.FluentMap;
@@ -163,6 +171,7 @@ final class DefaultApplicationsTest extends AbstractOperationsTest {
new DefaultApplications(
Mono.just(this.cloudFoundryClient),
Mono.just(this.dopplerClient),
+ Mono.just(this.logCacheClient),
this.randomWords,
Mono.just(TEST_SPACE_ID));
@@ -1306,8 +1315,9 @@ void listTasks() {
.verify(Duration.ofSeconds(5));
}
+ @SuppressWarnings("deprecation")
@Test
- void logs() {
+ void logsDoppler() {
requestApplications(
this.cloudFoundryClient,
"test-application-name",
@@ -1318,13 +1328,14 @@ void logs() {
this.applications
.logs(LogsRequest.builder().name("test-application-name").recent(false).build())
.as(StepVerifier::create)
- .expectNext(fill(LogMessage.builder(), "log-message-").build())
+ .expectNextMatches(log -> log.getMessage().equals("test-log-message-message"))
.expectComplete()
.verify(Duration.ofSeconds(5));
}
+ @SuppressWarnings("deprecation")
@Test
- void logsNoApp() {
+ void logsNoAppDoppler() {
requestApplicationsEmpty(this.cloudFoundryClient, "test-application-name", TEST_SPACE_ID);
this.applications
@@ -1339,8 +1350,9 @@ void logsNoApp() {
.verify(Duration.ofSeconds(5));
}
+ @SuppressWarnings("deprecation")
@Test
- void logsRecent() {
+ void logsRecentDoppler() {
requestApplications(
this.cloudFoundryClient,
"test-application-name",
@@ -1351,13 +1363,38 @@ void logsRecent() {
this.applications
.logs(LogsRequest.builder().name("test-application-name").recent(true).build())
.as(StepVerifier::create)
- .expectNext(fill(LogMessage.builder(), "log-message-").build())
+ .expectNextMatches(log -> log.getMessage().equals("test-log-message-message"))
.expectComplete()
.verify(Duration.ofSeconds(5));
}
@Test
- void logsRecentNotSet() {
+ void logsLogCache() {
+ requestApplications(
+ this.cloudFoundryClient,
+ "test-application-name",
+ TEST_SPACE_ID,
+ "test-metadata-id");
+ requestLogsRecentLogCache(this.logCacheClient, "test-metadata-id");
+
+ this.applications
+ .logs(ApplicationLogsRequest.builder().name("test-application-name").build())
+ .as(StepVerifier::create)
+ .expectNextMatches(
+ log ->
+ log.getMessage().equals("test-payload")
+ && log.getLogType() == ApplicationLogType.OUT
+ && log.getSourceId().equals("test-sourceId")
+ && log.getInstanceId().equals("test-instanceId")
+ && log.getSourceType().equals("APP/PROC/WEB")
+ && log.getTimestamp() == 1L)
+ .expectComplete()
+ .verify(Duration.ofSeconds(5));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ void logsRecentNotSetDoppler() {
requestApplications(
this.cloudFoundryClient,
"test-application-name",
@@ -5317,12 +5354,13 @@ private static void requestListTasksEmpty(
.build()));
}
+ @SuppressWarnings("deprecation")
private static void requestLogsRecent(DopplerClient dopplerClient, String applicationId) {
when(dopplerClient.recentLogs(
RecentLogsRequest.builder().applicationId(applicationId).build()))
.thenReturn(
Flux.just(
- Envelope.builder()
+ org.cloudfoundry.doppler.Envelope.builder()
.eventType(EventType.LOG_MESSAGE)
.logMessage(
fill(LogMessage.builder(), "log-message-").build())
@@ -5330,11 +5368,42 @@ private static void requestLogsRecent(DopplerClient dopplerClient, String applic
.build()));
}
+ private static void requestLogsRecentLogCache(LogCacheClient logCacheClient, String sourceId) {
+ String base64Payload =
+ Base64.getEncoder().encodeToString("test-payload".getBytes(StandardCharsets.UTF_8));
+ when(logCacheClient.read(ReadRequest.builder().sourceId(sourceId).build()))
+ .thenReturn(
+ Mono.just(
+ fill(ReadResponse.builder())
+ .envelopes(
+ fill(EnvelopeBatch.builder())
+ .batch(
+ Arrays.asList(
+ fill(Envelope.builder())
+ .tags(
+ Collections
+ .singletonMap(
+ "source_type",
+ "APP/PROC/WEB"))
+ .log(
+ Log
+ .builder()
+ .payload(
+ base64Payload)
+ .type(
+ LogType
+ .OUT)
+ .build())
+ .build()))
+ .build())
+ .build()));
+ }
+
private static void requestLogsStream(DopplerClient dopplerClient, String applicationId) {
when(dopplerClient.stream(StreamRequest.builder().applicationId(applicationId).build()))
.thenReturn(
Flux.just(
- Envelope.builder()
+ org.cloudfoundry.doppler.Envelope.builder()
.eventType(EventType.LOG_MESSAGE)
.logMessage(
fill(LogMessage.builder(), "log-message-").build())
diff --git a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java
index c0b3f44b30..da390ac204 100644
--- a/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java
+++ b/integration-test/src/test/java/org/cloudfoundry/IntegrationTestConfiguration.java
@@ -48,6 +48,7 @@
import org.cloudfoundry.client.v2.stacks.StackResource;
import org.cloudfoundry.client.v2.userprovidedserviceinstances.CreateUserProvidedServiceInstanceRequest;
import org.cloudfoundry.doppler.DopplerClient;
+import org.cloudfoundry.logcache.v1.LogCacheClient;
import org.cloudfoundry.logcache.v1.TestLogCacheEndpoints;
import org.cloudfoundry.networking.NetworkingClient;
import org.cloudfoundry.operations.DefaultCloudFoundryOperations;
@@ -266,6 +267,7 @@ ReactorCloudFoundryClient cloudFoundryClient(
DefaultCloudFoundryOperations cloudFoundryOperations(
CloudFoundryClient cloudFoundryClient,
DopplerClient dopplerClient,
+ LogCacheClient logCacheClient,
NetworkingClient networkingClient,
RoutingClient routingClient,
UaaClient uaaClient,
@@ -274,6 +276,7 @@ DefaultCloudFoundryOperations cloudFoundryOperations(
return DefaultCloudFoundryOperations.builder()
.cloudFoundryClient(cloudFoundryClient)
.dopplerClient(dopplerClient)
+ .logCacheClient(logCacheClient)
.networkingClient(networkingClient)
.routingClient(routingClient)
.uaaClient(uaaClient)
diff --git a/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java b/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java
index 36e1bd9456..37c701dbbe 100644
--- a/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java
+++ b/integration-test/src/test/java/org/cloudfoundry/operations/ApplicationsTest.java
@@ -30,14 +30,6 @@
import org.cloudfoundry.CloudFoundryVersion;
import org.cloudfoundry.IfCloudFoundryVersion;
import org.cloudfoundry.client.CloudFoundryClient;
-import org.cloudfoundry.logcache.v1.Envelope;
-import org.cloudfoundry.logcache.v1.EnvelopeBatch;
-import org.cloudfoundry.logcache.v1.EnvelopeType;
-import org.cloudfoundry.logcache.v1.Log;
-import org.cloudfoundry.logcache.v1.LogCacheClient;
-import org.cloudfoundry.logcache.v1.LogType;
-import org.cloudfoundry.logcache.v1.ReadRequest;
-import org.cloudfoundry.logcache.v1.ReadResponse;
import org.cloudfoundry.operations.applications.ApplicationDetail;
import org.cloudfoundry.operations.applications.ApplicationEnvironments;
import org.cloudfoundry.operations.applications.ApplicationEvent;
@@ -108,7 +100,6 @@ public final class ApplicationsTest extends AbstractIntegrationTest {
@Autowired private String serviceName;
- @Autowired private LogCacheClient logCacheClient;
@Autowired private CloudFoundryClient cloudFoundryClient;
// To create a service in #pushBindService, the Service Broker must be installed first.
@@ -508,11 +499,13 @@ public void listTasks() throws IOException {
}
/**
- * Doppler was dropped in PCF 4.x in favor of logcache. This test does not work
- * on TAS 4.x.
+ * Exercise the LogCache client via {@code logs(ApplicationLogsRequest)}.
+ * LogCache has been a default cf-deployment component since v3.0.0 (July 2018),
+ * with the {@code /api/v1/read} endpoint available since log-cache-release v2.0.0
+ * (October 2018).
*/
@Test
- @IfCloudFoundryVersion(lessThan = CloudFoundryVersion.PCF_4_v2)
+ @IfCloudFoundryVersion(greaterThanOrEqualTo = CloudFoundryVersion.PCF_2_3)
public void logs() throws IOException {
String applicationName = this.nameFactory.getApplicationName();
@@ -537,45 +530,6 @@ public void logs() throws IOException {
.verify(Duration.ofMinutes(5));
}
- /**
- * Exercise the LogCache client. Serves as a reference for using the logcache client,
- * and will help with the transition to the new
- * {@link org.cloudfoundry.operations.applications.Applications#logs(ApplicationLogsRequest)}.
- */
- @Test
- public void logCacheLogs() throws IOException {
- String applicationName = this.nameFactory.getApplicationName();
-
- createApplication(
- this.cloudFoundryOperations,
- new ClassPathResource("test-application.zip").getFile().toPath(),
- applicationName,
- false)
- .then(
- this.cloudFoundryOperations
- .applications()
- .get(GetApplicationRequest.builder().name(applicationName).build()))
- .map(ApplicationDetail::getId)
- .flatMapMany(
- appGuid ->
- this.logCacheClient.read(
- ReadRequest.builder()
- .sourceId(appGuid)
- .envelopeType(EnvelopeType.LOG)
- .limit(1)
- .build()))
- .map(ReadResponse::getEnvelopes)
- .map(EnvelopeBatch::getBatch)
- .flatMap(Flux::fromIterable)
- .map(Envelope::getLog)
- .map(Log::getType)
- .next()
- .as(StepVerifier::create)
- .expectNext(LogType.OUT)
- .expectComplete()
- .verify(Duration.ofMinutes(5));
- }
-
@Test
public void pushBindServices() throws IOException {
String applicationName = this.nameFactory.getApplicationName();