8282import org .cloudfoundry .client .v2 .stacks .GetStackResponse ;
8383import org .cloudfoundry .client .v2 .stacks .ListStacksRequest ;
8484import org .cloudfoundry .client .v2 .stacks .StackResource ;
85+ import org .cloudfoundry .client .v3 .BuildpackData ;
86+ import org .cloudfoundry .client .v3 .Lifecycle ;
8587import org .cloudfoundry .client .v3 .Resource ;
8688import org .cloudfoundry .client .v3 .applications .ApplicationResource ;
89+ import org .cloudfoundry .client .v3 .applications .GetApplicationResponse ;
8790import org .cloudfoundry .client .v3 .applications .ListApplicationsRequest ;
8891import org .cloudfoundry .client .v3 .tasks .CancelTaskRequest ;
8992import org .cloudfoundry .client .v3 .tasks .CancelTaskResponse ;
111114import reactor .core .publisher .Flux ;
112115import reactor .core .publisher .Mono ;
113116import reactor .util .function .Tuple2 ;
114- import reactor .util .function .Tuple4 ;
117+ import reactor .util .function .Tuple5 ;
115118import reactor .util .function .Tuples ;
116119
117120import java .io .IOException ;
132135import java .util .function .UnaryOperator ;
133136import java .util .stream .Collectors ;
134137
138+ import static org .cloudfoundry .client .v3 .LifecycleType .BUILDPACK ;
135139import static org .cloudfoundry .util .DelayUtils .exponentialBackOff ;
136140import static org .cloudfoundry .util .tuple .TupleUtils .function ;
137141import 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
0 commit comments