Skip to content
This repository was archived by the owner on Sep 12, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<spring-security.version>6.4.5</spring-security.version>
<commons-io.version>2.18.0</commons-io.version>
<immutables.version>2.10.1</immutables.version>
<cloudfoundry-client.version>5.12.2.RELEASE</cloudfoundry-client.version>
<cloudfoundry-client.version>5.13.0.RELEASE</cloudfoundry-client.version>
<reactor-netty.version>1.2.5</reactor-netty.version>
<micrometer.version>1.12.5</micrometer.version>
<commons-logging.version>1.3.1</commons-logging.version>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.sap.cloudfoundry.client.facade.adapters;

import java.util.HashMap;
import java.util.Map;

import org.cloudfoundry.client.v3.BuildpackData;
import org.cloudfoundry.client.v3.CnbData;
import org.cloudfoundry.client.v3.LifecycleData;
import org.cloudfoundry.client.v3.applications.Application;
import org.cloudfoundry.client.v3.applications.ApplicationState;
import org.immutables.value.Value;
Expand All @@ -13,12 +18,12 @@
import com.sap.cloudfoundry.client.facade.domain.Lifecycle;
import com.sap.cloudfoundry.client.facade.domain.LifecycleType;

import java.util.HashMap;
import java.util.Map;

@Value.Immutable
public abstract class RawCloudApplication extends RawCloudEntity<CloudApplication> {

public static final String BUILDPACKS = "buildpacks";
public static final String STACK = "stack";

public abstract Application getApplication();

public abstract Derivable<CloudSpace> getSpace();
Expand All @@ -42,11 +47,12 @@ private static CloudApplication.State parseState(ApplicationState state) {

private static Lifecycle parseLifecycle(org.cloudfoundry.client.v3.Lifecycle lifecycle) {
Map<String, Object> data = new HashMap<>();
if (lifecycle.getType() == org.cloudfoundry.client.v3.LifecycleType.BUILDPACK) {
var buildpackData = (BuildpackData) lifecycle.getData();
data.put("buildpacks", buildpackData.getBuildpacks());
data.put("stack", buildpackData.getStack());
org.cloudfoundry.client.v3.LifecycleType lifecycleType = lifecycle.getType();

if (isBuildpackOrCnb(lifecycleType)) {
addLifecycleData(data, lifecycle.getData());
}

return ImmutableLifecycle.builder()
.type(LifecycleType.valueOf(lifecycle.getType()
.toString()
Expand All @@ -55,4 +61,19 @@ private static Lifecycle parseLifecycle(org.cloudfoundry.client.v3.Lifecycle lif
.build();
}

private static boolean isBuildpackOrCnb(org.cloudfoundry.client.v3.LifecycleType lifecycleType) {
return lifecycleType == org.cloudfoundry.client.v3.LifecycleType.BUILDPACK
|| lifecycleType == org.cloudfoundry.client.v3.LifecycleType.CNB;
}

private static void addLifecycleData(Map<String, Object> data, LifecycleData lifecycleData) {
if (lifecycleData instanceof BuildpackData buildpackData) {
data.put(BUILDPACKS, buildpackData.getBuildpacks());
data.put(STACK, buildpackData.getStack());
} else if (lifecycleData instanceof CnbData cnbData) {
data.put(BUILDPACKS, cnbData.getBuildpacks());
data.put(STACK, cnbData.getStack());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public enum LifecycleType {

BUILDPACK, DOCKER, KPACK;
BUILDPACK, DOCKER, KPACK, CNB;

public String toString() {
return this.name().toLowerCase();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public interface Staging {
@Nullable
Integer getInvocationTimeout();

@Nullable
LifecycleType getLifecycleType();

default String getBuildpack() {
return getBuildpacks().isEmpty() ? null : getBuildpacks().get(0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,26 @@ public void createApplication(ApplicationToCreateDto applicationToCreateDto) {
}

private Lifecycle buildApplicationLifecycle(Staging staging) {
return staging.getDockerInfo() != null ? createDockerLifecycle() : createBuildpackLifecycle(staging);
// Prioritize Docker lifecycle if DockerInfo is provided
if (staging.getDockerInfo() != null) {
return createDockerLifecycle();
}

// Determine lifecycle type, defaulting to BUILDPACK if lifecycleType is null
LifecycleType lifecycleType = staging.getLifecycleType() != null ? LifecycleType.valueOf(staging.getLifecycleType()
.name())
: LifecycleType.BUILDPACK;

return createLifecycleByType(staging, lifecycleType);
}

private Lifecycle createLifecycleByType(Staging staging, LifecycleType lifecycleType) {
validateLifecycleConfiguration(staging, lifecycleType);
BuildpackData buildpackData = createBuildpackData(staging);
return Lifecycle.builder()
.type(lifecycleType)
.data(buildpackData)
.build();
}

private Lifecycle createDockerLifecycle() {
Expand All @@ -323,12 +342,10 @@ private Lifecycle createDockerLifecycle() {
.build();
}

private Lifecycle createBuildpackLifecycle(Staging staging) {
BuildpackData buildpackData = createBuildpackData(staging);
return Lifecycle.builder()
.type(LifecycleType.BUILDPACK)
.data(buildpackData)
.build();
private void validateLifecycleConfiguration(Staging staging, LifecycleType lifecycleType) {
if (lifecycleType == LifecycleType.CNB && staging.getBuildpacks() == null) {
throw new IllegalArgumentException("Buildpacks are required for CNB lifecycle type.");
}
}

private BuildpackData createBuildpackData(Staging staging) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.sap.cloudfoundry.client.facade.domain.CloudBuild;
import com.sap.cloudfoundry.client.facade.domain.CloudMetadata;
import com.sap.cloudfoundry.client.facade.domain.CloudPackage;
import com.sap.cloudfoundry.client.facade.domain.CloudProcess;
import com.sap.cloudfoundry.client.facade.domain.CloudRoute;
import com.sap.cloudfoundry.client.facade.domain.DockerInfo;
import com.sap.cloudfoundry.client.facade.domain.ImmutableCloudApplication;
Expand All @@ -45,10 +46,10 @@
import com.sap.cloudfoundry.client.facade.domain.ImmutableDockerInfo;
import com.sap.cloudfoundry.client.facade.domain.ImmutableStaging;
import com.sap.cloudfoundry.client.facade.domain.InstancesInfo;
import com.sap.cloudfoundry.client.facade.domain.LifecycleType;
import com.sap.cloudfoundry.client.facade.domain.Staging;
import com.sap.cloudfoundry.client.facade.domain.Status;
import com.sap.cloudfoundry.client.facade.dto.ApplicationToCreateDto;
import com.sap.cloudfoundry.client.facade.domain.CloudProcess;
import com.sap.cloudfoundry.client.facade.dto.ImmutableApplicationToCreateDto;

class ApplicationsCloudControllerClientIntegrationTest extends CloudControllerClientIntegrationTest {
Expand Down Expand Up @@ -157,11 +158,10 @@ void updateApplicationHealthcheckType() {
createAndVerifyDefaultApplication(applicationName);
UUID applicationGuid = client.getApplicationGuid(applicationName);
delegate.applicationsV2()
.update(org.cloudfoundry.client.v2.applications.UpdateApplicationRequest
.builder()
.applicationId(applicationGuid.toString())
.healthCheckType(HealthCheckType.NONE.getValue())
.build())
.update(org.cloudfoundry.client.v2.applications.UpdateApplicationRequest.builder()
.applicationId(applicationGuid.toString())
.healthCheckType(HealthCheckType.NONE.getValue())
.build())
.block();
CloudProcess cloudProcess = client.getApplicationProcess(applicationGuid);
assertEquals(com.sap.cloudfoundry.client.facade.domain.HealthCheckType.NONE, cloudProcess.getHealthCheckType());
Expand Down Expand Up @@ -422,6 +422,24 @@ void createApplicationWithBuildpacks() {
}
}

@Test
@DisplayName("Create application with CNB lifecycle and verify attributes")
void createCnbApplication() {
String applicationName = "test-app-17";
Staging staging = ImmutableStaging.builder()
.lifecycleType(LifecycleType.CNB)
.addBuildpacks(JAVA_BUILDPACK, STATICFILE_BUILDPACK)
.build();
CloudRoute route = getImmutableCloudRoute();
try {
verifyApplicationWillBeCreated(applicationName, staging, Set.of(route));
} catch (Exception e) {
fail(e);
} finally {
client.deleteApplication(applicationName);
}
}

private void verifyApplicationWillBeCreated(String applicationName, Staging staging, Set<CloudRoute> routes) {
ApplicationToCreateDto applicationToCreateDto = ImmutableApplicationToCreateDto.builder()
.name(applicationName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import java.util.HashMap;
import java.util.Map;

import com.sap.cloudfoundry.client.facade.rest.CloudControllerRestClient;
import com.sap.cloudfoundry.client.facade.rest.CloudControllerRestClientFactory;
import org.cloudfoundry.client.CloudFoundryClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -70,11 +68,18 @@ protected Lifecycle createLifecycle(Staging staging) {
.data(Map.of())
.build();
}

var data = new HashMap<String, Object>();
data.put("buildpacks", staging.getBuildpacks());
data.put("stack", DEFAULT_STACK);

// Default to BUILDPACK if lifecycleType is null
LifecycleType lifecycleType = staging.getLifecycleType() != null
? staging.getLifecycleType()
: LifecycleType.BUILDPACK;

return ImmutableLifecycle.builder()
.type(LifecycleType.BUILDPACK)
.type(lifecycleType)
.data(data)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,24 @@ class RawCloudApplicationTest {
.name(SPACE_NAME)
.build();
private static final String EXPECTED_BUILDPACK = "ruby_buildpack";
private static final String BUILDPACK_URL = "custom-buildpack-url";

private static final String EXPECTED_STACK = "cflinuxfs3";
private static final CloudApplication.State EXPECTED_STATE = CloudApplication.State.STARTED;

@Test
void testDeriveForBuildpackApp() {
RawCloudEntityTest.testDerive(buildApplication(buildBuildpackLifecycle()),
buildRawApplication(buildBuildpackLifecycleResource()));
RawCloudEntityTest.testDerive(buildApplication(buildBuildpackLifecycle()), buildRawApplication(buildBuildpackLifecycleResource()));
}

@Test
void testDeriveForDockerApp() {
RawCloudEntityTest.testDerive(buildApplication(buildDockerLifecycle()),
buildRawApplication(buildDockerLifecycleResource()));
RawCloudEntityTest.testDerive(buildApplication(buildDockerLifecycle()), buildRawApplication(buildDockerLifecycleResource()));
}

@Test
void testDeriveForCnbApp() {
RawCloudEntityTest.testDerive(buildApplication(buildCnbLifecycle()), buildRawApplication(buildCnbLifecycleResource()));
}

private static CloudApplication buildApplication(com.sap.cloudfoundry.client.facade.domain.Lifecycle lifecycle) {
Expand All @@ -56,13 +61,15 @@ private static CloudApplication buildApplication(com.sap.cloudfoundry.client.fac
private static com.sap.cloudfoundry.client.facade.domain.Lifecycle buildBuildpackLifecycle() {
return ImmutableLifecycle.builder()
.type(com.sap.cloudfoundry.client.facade.domain.LifecycleType.BUILDPACK)
.data(buildBuildpackLifecycleData())
.data(buildLifecycleData(EXPECTED_BUILDPACK))
.build();
}

private static Map<String, Object> buildBuildpackLifecycleData() {
return Map.of("buildpacks", List.of(EXPECTED_BUILDPACK),
"stack", EXPECTED_STACK);
private com.sap.cloudfoundry.client.facade.domain.Lifecycle buildCnbLifecycle() {
return ImmutableLifecycle.builder()
.type(com.sap.cloudfoundry.client.facade.domain.LifecycleType.CNB)
.data(buildLifecycleData(BUILDPACK_URL))
.build();
}

private static com.sap.cloudfoundry.client.facade.domain.Lifecycle buildDockerLifecycle() {
Expand All @@ -72,6 +79,10 @@ private static com.sap.cloudfoundry.client.facade.domain.Lifecycle buildDockerLi
.build();
}

private static Map<String, Object> buildLifecycleData(String buildpack) {
return Map.of("buildpacks", List.of(buildpack), "stack", EXPECTED_STACK);
}

private static RawCloudApplication buildRawApplication(Lifecycle lifecycle) {
return ImmutableRawCloudApplication.builder()
.application(buildApplicationResource(lifecycle))
Expand Down Expand Up @@ -101,6 +112,16 @@ private static Lifecycle buildBuildpackLifecycleResource() {
.build();
}

private Lifecycle buildCnbLifecycleResource() {
return Lifecycle.builder()
.type(LifecycleType.CNB)
.data(BuildpackData.builder()
.buildpack(BUILDPACK_URL)
.stack(STACK_NAME)
.build())
.build();
}

private static Lifecycle buildDockerLifecycleResource() {
return Lifecycle.builder()
.type(LifecycleType.DOCKER)
Expand Down