Skip to content

Commit 7ef9be7

Browse files
committed
Merge branch '3.x' into main
2 parents ac2411b + 4072ac5 commit 7ef9be7

19 files changed

Lines changed: 422 additions & 153 deletions

File tree

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/ApplicationManifestUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ private static <T> void putIfPresent(Map<String, Object> yaml, String key, T val
323323

324324
@SuppressWarnings("unchecked")
325325
private static ApplicationManifest.Builder toApplicationManifest(Map<String, Object> application, ApplicationManifest.Builder builder, Path root) {
326-
asString(application, "buildpack", builder::buildpack);
326+
asListOfString(application, "buildpacks", builder::buildpacks);
327+
asString(application, "buildpack", builder::buildpacks);
327328
asString(application, "command", builder::command);
328329
asMemoryInteger(application, "disk_quota", builder::disk);
329330
asDocker(application, "docker", builder::docker);
@@ -358,7 +359,7 @@ private static List<Map<String, String>> toRoutesYaml(List<Route> routes) {
358359
private static Map<String, Object> toYaml(ApplicationManifest applicationManifest) {
359360
Map<String, Object> yaml = new TreeMap<>();
360361

361-
putIfPresent(yaml, "buildpack", applicationManifest.getBuildpack());
362+
putIfPresent(yaml, "buildpacks", applicationManifest.getBuildpacks());
362363
putIfPresent(yaml, "command", applicationManifest.getCommand());
363364
Integer disk = applicationManifest.getDisk();
364365
if (null != disk) {

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/DefaultApplications.java

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@
8282
import org.cloudfoundry.client.v2.stacks.GetStackResponse;
8383
import org.cloudfoundry.client.v2.stacks.ListStacksRequest;
8484
import org.cloudfoundry.client.v2.stacks.StackResource;
85+
import org.cloudfoundry.client.v3.BuildpackData;
86+
import org.cloudfoundry.client.v3.Lifecycle;
8587
import org.cloudfoundry.client.v3.Resource;
8688
import org.cloudfoundry.client.v3.applications.ApplicationResource;
89+
import org.cloudfoundry.client.v3.applications.GetApplicationResponse;
8790
import org.cloudfoundry.client.v3.applications.ListApplicationsRequest;
8891
import org.cloudfoundry.client.v3.tasks.CancelTaskRequest;
8992
import org.cloudfoundry.client.v3.tasks.CancelTaskResponse;
@@ -111,7 +114,7 @@
111114
import reactor.core.publisher.Flux;
112115
import reactor.core.publisher.Mono;
113116
import reactor.util.function.Tuple2;
114-
import reactor.util.function.Tuple4;
117+
import reactor.util.function.Tuple5;
115118
import reactor.util.function.Tuples;
116119

117120
import java.io.IOException;
@@ -132,6 +135,7 @@
132135
import java.util.function.UnaryOperator;
133136
import java.util.stream.Collectors;
134137

138+
import static org.cloudfoundry.client.v3.LifecycleType.BUILDPACK;
135139
import static org.cloudfoundry.util.DelayUtils.exponentialBackOff;
136140
import static org.cloudfoundry.util.tuple.TupleUtils.function;
137141
import static org.cloudfoundry.util.tuple.TupleUtils.predicate;
@@ -269,9 +273,11 @@ public Mono<ApplicationManifest> getApplicationManifest(GetApplicationManifestRe
269273
)))
270274
.flatMap(function((cloudFoundryClient, applicationId) -> Mono.zip(
271275
Mono.just(cloudFoundryClient),
276+
Mono.just(applicationId),
272277
requestApplicationSummary(cloudFoundryClient, applicationId)
273278
)))
274-
.flatMap(function((cloudFoundryClient, response) -> Mono.zip(
279+
.flatMap(function((cloudFoundryClient, applicationId, response) -> Mono.zip(
280+
getApplicationBuildpacks(cloudFoundryClient, applicationId),
275281
Mono.just(response),
276282
getStackName(cloudFoundryClient, response.getStackId())
277283
)))
@@ -358,7 +364,7 @@ public Flux<LogMessage> logs(LogsRequest request) {
358364
@SuppressWarnings("deprecation")
359365
public Mono<Void> push(PushApplicationRequest request) {
360366
ApplicationManifest.Builder builder = ApplicationManifest.builder()
361-
.buildpack(request.getBuildpack())
367+
.buildpacks(request.getBuildpacks())
362368
.command(request.getCommand())
363369
.disk(request.getDiskQuota())
364370
.docker(Docker.builder()
@@ -762,6 +768,19 @@ private static Mono<AbstractApplicationResource> getApplication(CloudFoundryClie
762768
.onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Application %s does not exist", application));
763769
}
764770

771+
private static Mono<List<String>> getApplicationBuildpacks(CloudFoundryClient cloudFoundryClient, String applicationId) {
772+
return cloudFoundryClient.applicationsV3()
773+
.get(org.cloudfoundry.client.v3.applications.GetApplicationRequest.builder()
774+
.applicationId(applicationId)
775+
.build())
776+
.map(GetApplicationResponse::getLifecycle)
777+
.filter(lifecycle -> BUILDPACK == lifecycle.getType())
778+
.map(Lifecycle::getData)
779+
.cast(BuildpackData.class)
780+
.map(BuildpackData::getBuildpacks)
781+
.defaultIfEmpty(Collections.emptyList());
782+
}
783+
765784
private static Mono<String> getApplicationId(CloudFoundryClient cloudFoundryClient, String application, String spaceId) {
766785
return getApplication(cloudFoundryClient, application, spaceId)
767786
.map(ResourceUtils::getId);
@@ -822,9 +841,8 @@ private static Mono<ApplicationResource> getApplicationV3(CloudFoundryClient clo
822841
.onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Application %s does not exist", application));
823842
}
824843

825-
private static Mono<Tuple4<SummaryApplicationResponse, GetStackResponse, List<InstanceDetail>, List<String>>>
826-
getAuxiliaryContent(CloudFoundryClient cloudFoundryClient, AbstractApplicationResource applicationResource) {
827-
844+
private static Mono<Tuple5<List<String>, SummaryApplicationResponse, GetStackResponse, List<InstanceDetail>, List<String>>> getAuxiliaryContent(CloudFoundryClient cloudFoundryClient,
845+
AbstractApplicationResource applicationResource) {
828846
String applicationId = ResourceUtils.getId(applicationResource);
829847
String stackId = ResourceUtils.getEntity(applicationResource).getStackId();
830848

@@ -835,19 +853,14 @@ private static Mono<ApplicationResource> getApplicationV3(CloudFoundryClient clo
835853
getApplicationInstances(cloudFoundryClient, applicationId)
836854
)
837855
.flatMap(function((applicationStatisticsResponse, summaryApplicationResponse, applicationInstancesResponse) -> Mono.zip(
856+
getApplicationBuildpacks(cloudFoundryClient, applicationId),
838857
Mono.just(summaryApplicationResponse),
839858
requestStack(cloudFoundryClient, stackId),
840859
toInstanceDetailList(applicationInstancesResponse, applicationStatisticsResponse),
841860
toUrls(summaryApplicationResponse.getRoutes())
842861
)));
843862
}
844863

845-
private static String getBuildpack(SummaryApplicationResponse response) {
846-
return Optional
847-
.ofNullable(response.getBuildpack())
848-
.orElse(response.getDetectedBuildpack());
849-
}
850-
851864
private static Mono<String> getDefaultDomainId(CloudFoundryClient cloudFoundryClient) {
852865
return requestSharedDomains(cloudFoundryClient)
853866
.filter(resource -> !Optional.ofNullable(ResourceUtils.getEntity(resource).getInternal()).orElse(false))
@@ -1172,8 +1185,9 @@ private static Flux<Void> pushApplication(CloudFoundryClient cloudFoundryClient,
11721185
randomWords, spaceId)
11731186
.thenReturn(Tuples.of(applicationId, matchedResources))))
11741187
.flatMap(function((applicationId, matchedResources) -> Mono.when(
1175-
uploadApplicationAndWait(cloudFoundryClient, applicationId, manifest.getPath(), matchedResources, request.getStagingTimeout()),
1176-
bindServices(cloudFoundryClient, applicationId, manifest, spaceId)
1188+
bindServices(cloudFoundryClient, applicationId, manifest, spaceId),
1189+
updateBuildpacks(cloudFoundryClient, applicationId, manifest),
1190+
uploadApplicationAndWait(cloudFoundryClient, applicationId, manifest.getPath(), matchedResources, request.getStagingTimeout())
11771191
)
11781192
.thenReturn(applicationId)))
11791193
.flatMap(applicationId -> stopAndStartApplication(cloudFoundryClient, applicationId, manifest.getName(), request));
@@ -1283,7 +1297,6 @@ private static Mono<CopyApplicationResponse> requestCopyBits(CloudFoundryClient
12831297

12841298
private static Mono<CreateApplicationResponse> requestCreateApplication(CloudFoundryClient cloudFoundryClient, ApplicationManifest manifest, String spaceId, String stackId) {
12851299
CreateApplicationRequest.Builder builder = CreateApplicationRequest.builder()
1286-
.buildpack(manifest.getBuildpack())
12871300
.command(manifest.getCommand())
12881301
.diskQuota(manifest.getDisk())
12891302
.environmentJsons(manifest.getEnvironmentVariables())
@@ -1296,6 +1309,10 @@ private static Mono<CreateApplicationResponse> requestCreateApplication(CloudFou
12961309
.spaceId(spaceId)
12971310
.stackId(stackId);
12981311

1312+
if (manifest.getBuildpacks() != null && manifest.getBuildpacks().size() == 1) {
1313+
builder.buildpack(manifest.getBuildpacks().get(0));
1314+
}
1315+
12991316
if (manifest.getDocker() != null) {
13001317
Optional.ofNullable(manifest.getDocker().getImage())
13011318
.ifPresent(image -> {
@@ -1584,7 +1601,6 @@ private static Mono<AbstractApplicationResource> requestUpdateApplication(CloudF
15841601
ApplicationManifest manifest, String stackId) {
15851602
return requestUpdateApplication(cloudFoundryClient, applicationId, builder -> {
15861603
builder
1587-
.buildpack(manifest.getBuildpack())
15881604
.command(manifest.getCommand())
15891605
.diskQuota(manifest.getDisk())
15901606
.environmentJsons(environmentJsons)
@@ -1596,6 +1612,10 @@ private static Mono<AbstractApplicationResource> requestUpdateApplication(CloudF
15961612
.name(manifest.getName())
15971613
.stackId(stackId);
15981614

1615+
if (manifest.getBuildpacks() != null && manifest.getBuildpacks().size() == 1) {
1616+
builder.buildpack(manifest.getBuildpacks().get(0));
1617+
}
1618+
15991619
if (manifest.getDocker() != null) {
16001620
Optional.ofNullable(manifest.getDocker().getImage())
16011621
.ifPresent(image -> {
@@ -1649,8 +1669,8 @@ private static Mono<AbstractApplicationResource> requestUpdateApplicationState(C
16491669
return requestUpdateApplication(cloudFoundryClient, applicationId, builder -> builder.state(state));
16501670
}
16511671

1652-
private static Mono<UploadApplicationResponse> requestUploadApplication(CloudFoundryClient cloudFoundryClient, String applicationId, Path application, List<ResourceMatchingUtils
1653-
.ArtifactMetadata> matchedResources) {
1672+
private static Mono<UploadApplicationResponse> requestUploadApplication(CloudFoundryClient cloudFoundryClient, String applicationId, Path application,
1673+
List<ResourceMatchingUtils.ArtifactMetadata> matchedResources) {
16541674
UploadApplicationRequest request = matchedResources.stream()
16551675
.reduce(UploadApplicationRequest.builder()
16561676
.application(application)
@@ -1712,10 +1732,14 @@ private static Mono<AbstractApplicationResource> stopApplicationIfNotStopped(Clo
17121732
return isNotIn(resource, STOPPED_STATE) ? stopApplication(cloudFoundryClient, ResourceUtils.getId(resource)) : Mono.just(resource);
17131733
}
17141734

1715-
private static ApplicationDetail toApplicationDetail(SummaryApplicationResponse summaryApplicationResponse, GetStackResponse getStackResponse, List<InstanceDetail> instanceDetails,
1716-
List<String> urls) {
1735+
private static ApplicationDetail toApplicationDetail(List<String> buildpacks, SummaryApplicationResponse summaryApplicationResponse, GetStackResponse getStackResponse,
1736+
List<InstanceDetail> instanceDetails, List<String> urls) {
1737+
if (buildpacks.size() == 0) {
1738+
buildpacks = Collections.singletonList(summaryApplicationResponse.getDetectedBuildpack());
1739+
}
1740+
17171741
return ApplicationDetail.builder()
1718-
.buildpack(getBuildpack(summaryApplicationResponse))
1742+
.buildpacks(buildpacks)
17191743
.diskQuota(summaryApplicationResponse.getDiskQuota())
17201744
.id(summaryApplicationResponse.getId())
17211745
.instanceDetails(instanceDetails)
@@ -1739,9 +1763,8 @@ private static ApplicationEnvironments toApplicationEnvironments(ApplicationEnvi
17391763
.build();
17401764
}
17411765

1742-
private static Mono<ApplicationManifest> toApplicationManifest(SummaryApplicationResponse response, String stackName) {
1743-
ApplicationManifest.Builder manifestBuilder = ApplicationManifest.builder()
1744-
.buildpack(response.getBuildpack())
1766+
private static Mono<ApplicationManifest> toApplicationManifest(List<String> buildpacks, SummaryApplicationResponse response, String stackName) {
1767+
ApplicationManifest.Builder builder = ApplicationManifest.builder()
17451768
.command(response.getCommand())
17461769
.disk(response.getDiskQuota())
17471770
.environmentVariables(response.getEnvironmentJsons())
@@ -1754,22 +1777,26 @@ private static Mono<ApplicationManifest> toApplicationManifest(SummaryApplicatio
17541777
.stack(stackName)
17551778
.timeout(response.getHealthCheckTimeout());
17561779

1780+
if (buildpacks != null && !buildpacks.isEmpty()) {
1781+
builder.buildpacks(buildpacks);
1782+
}
1783+
17571784
for (org.cloudfoundry.client.v2.routes.Route route : Optional.ofNullable(response.getRoutes()).orElse(Collections.emptyList())) {
1758-
manifestBuilder.route(Route.builder()
1785+
builder.route(Route.builder()
17591786
.route(toUrl(route))
17601787
.build());
17611788
}
17621789

17631790
if (Optional.ofNullable(response.getRoutes()).orElse(Collections.emptyList()).isEmpty()) {
1764-
manifestBuilder.noRoute(true);
1791+
builder.noRoute(true);
17651792
}
17661793

17671794
for (ServiceInstance service : Optional.ofNullable(response.getServices()).orElse(Collections.emptyList())) {
1768-
Optional.ofNullable(service.getName()).ifPresent(manifestBuilder::service);
1795+
Optional.ofNullable(service.getName()).ifPresent(builder::service);
17691796
}
17701797

17711798
return Mono
1772-
.just(manifestBuilder
1799+
.just(builder
17731800
.build());
17741801
}
17751802

@@ -1899,6 +1926,24 @@ private static Mono<List<String>> toUrls(List<org.cloudfoundry.client.v2.routes.
18991926
.collectList();
19001927
}
19011928

1929+
private static Mono<Void> updateBuildpacks(CloudFoundryClient cloudFoundryClient, String applicationId, ApplicationManifest manifest) {
1930+
if (manifest.getBuildpacks() == null || manifest.getBuildpacks().size() < 2) {
1931+
return Mono.empty();
1932+
}
1933+
1934+
return cloudFoundryClient.applicationsV3()
1935+
.update(org.cloudfoundry.client.v3.applications.UpdateApplicationRequest.builder()
1936+
.applicationId(applicationId)
1937+
.lifecycle(Lifecycle.builder()
1938+
.data(BuildpackData.builder()
1939+
.addAllBuildpacks(manifest.getBuildpacks())
1940+
.build())
1941+
.type(BUILDPACK)
1942+
.build())
1943+
.build())
1944+
.then();
1945+
}
1946+
19021947
private static Mono<Void> uploadApplicationAndWait(CloudFoundryClient cloudFoundryClient, String applicationId, Path application, List<ResourceMatchingUtils.ArtifactMetadata> matchedResources,
19031948
Duration stagingTimeout) {
19041949
return Mono

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/_ApplicationDetail.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.cloudfoundry.operations.applications;
1818

19+
import org.cloudfoundry.AllowNulls;
1920
import org.cloudfoundry.Nullable;
2021
import org.immutables.value.Value;
2122

@@ -29,10 +30,11 @@
2930
abstract class _ApplicationDetail extends AbstractApplicationSummary {
3031

3132
/**
32-
* The buildpack, if any, used to stage this application
33+
* The buildpacks, if any, used to stage this application
3334
*/
35+
@AllowNulls
3436
@Nullable
35-
abstract String getBuildpack();
37+
abstract List<String> getBuildpacks();
3638

3739
/**
3840
* The list of instances

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/_ApplicationManifest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void check() {
4949
}
5050

5151
if (getDocker() != null) {
52-
if (getDocker().getImage() != null && getBuildpack() != null) {
52+
if (getDocker().getImage() != null && getBuildpacks() != null && !getBuildpacks().isEmpty()) {
5353
throw new IllegalStateException("docker image and buildpack cannot both be set");
5454
}
5555

@@ -72,10 +72,10 @@ void check() {
7272
}
7373

7474
/**
75-
* The buildpack used by the application
75+
* The buildpacks used by the application
7676
*/
7777
@Nullable
78-
abstract String getBuildpack();
78+
abstract List<String> getBuildpacks();
7979

8080
/**
8181
* The command used to execute the application

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/applications/_PushApplicationRequest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.nio.file.Path;
2323
import java.time.Duration;
24+
import java.util.List;
2425

2526
/**
2627
* The request options for the push application operation
@@ -64,10 +65,10 @@ void check() {
6465
abstract Path getApplication();
6566

6667
/**
67-
* The buildpack for the application
68+
* The buildpacks for the application
6869
*/
6970
@Nullable
70-
abstract String getBuildpack();
71+
abstract List<String> getBuildpacks();
7172

7273
/**
7374
* The custom start command for the application

0 commit comments

Comments
 (0)