Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.cloudfoundry.multiapps.controller.client.lib.domain;

import java.util.List;

import com.sap.cloudfoundry.client.facade.CloudControllerClient;
import com.sap.cloudfoundry.client.facade.domain.CloudApplication;
import com.sap.cloudfoundry.client.facade.domain.DockerData;
import com.sap.cloudfoundry.client.facade.domain.LifecycleType;
import com.sap.cloudfoundry.client.facade.domain.Staging;

import java.util.List;

public class DropletInfoFactory {

public DropletInfo createDropletInfo(Staging staging) {
Expand All @@ -20,7 +20,7 @@ public DropletInfo createDropletInfo(Staging staging) {

public DropletInfo createDropletInfo(CloudApplication app, CloudControllerClient client) {
var lifecycle = app.getLifecycle();
if (lifecycle.getType() == LifecycleType.BUILDPACK) {
if (lifecycle.getType() == LifecycleType.BUILDPACK || lifecycle.getType() == LifecycleType.CNB) {
var buildpacks = (List<String>) lifecycle.getData()
.get("buildpacks");
var stack = (String) lifecycle.getData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ public final class Messages {
public static final String OBJECT_STORE_FILE_STORAGE_HEALTH_DATABASE_HEALTH = "Object store file storage health: \"{0}\", Database health: \"{1}\"";
public static final String ERROR_OCCURRED_DURING_OBJECT_STORE_HEALTH_CHECKING_FOR_INSTANCE = "Error occurred during object store health checking for instance: \"{0}\"";
public static final String ERROR_OCCURRED_WHILE_CHECKING_DATABASE_INSTANCE_0 = "Error occurred while checking database instance: \"{0}\"";
public static final String DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE = "Docker information must not be provided when lifecycle is set to \"{0}\"";
public static final String UNSUPPORTED_LIFECYCLE_VALUE = "Unsupported lifecycle value: \"{0}\"";
public static final String BUILDPACKS_REQUIRED_FOR_CNB = "Buildpacks must be provided when lifecycle is set to 'cnb'.";
public static final String DOCKER_INFO_REQUIRED = "Docker information must be provided when lifecycle is set to 'docker'.";
public static final String BUILDPACKS_NOT_ALLOWED_WITH_DOCKER = "Buildpacks must not be provided when lifecycle is set to 'docker'.";

// Warning messages
public static final String ENVIRONMENT_VARIABLE_IS_NOT_SET_USING_DEFAULT = "Environment variable \"{0}\" is not set. Using default \"{1}\"...";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class SupportedParameters {
public static final String BUILDPACK = "buildpack";
public static final String BUILDPACKS = "buildpacks";
public static final String STACK = "stack";
public static final String LIFECYCLE = "lifecycle";
public static final String HEALTH_CHECK_INVOCATION_TIMEOUT = "health-check-invocation-timeout";
public static final String HEALTH_CHECK_TIMEOUT = "health-check-timeout";
public static final String HEALTH_CHECK_TYPE = "health-check-type";
Expand Down Expand Up @@ -179,7 +180,7 @@ public class SupportedParameters {
@Deprecated
public static final String DEPRECATED_CONFIG_MTA_PROVIDES_DEPENDENCY = "mta-provides-dependency";

public static final Set<String> MODULE_PARAMETERS = Set.of(APP_NAME, APPLY_NAMESPACE, BUILDPACK, BUILDPACKS, COMMAND,
public static final Set<String> MODULE_PARAMETERS = Set.of(APP_NAME, APPLY_NAMESPACE, BUILDPACK, BUILDPACKS, LIFECYCLE, COMMAND,
CREATE_SERVICE_BROKER, DEFAULT_APP_NAME, DEFAULT_HOST, DEFAULT_INSTANCES,
DEFAULT_LIVE_APP_NAME, DEFAULT_LIVE_DOMAIN, DEFAULT_LIVE_HOST,
DEFAULT_LIVE_URI, DEFAULT_LIVE_URL, DEFAULT_URI, DEFAULT_URL,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
package org.cloudfoundry.multiapps.controller.core.parser;

import java.text.MessageFormat;
import java.util.List;
import java.util.Map;

import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters;
import org.cloudfoundry.multiapps.mta.util.PropertiesUtil;

import com.sap.cloudfoundry.client.facade.domain.DockerInfo;
import com.sap.cloudfoundry.client.facade.domain.ImmutableStaging;
import com.sap.cloudfoundry.client.facade.domain.LifecycleType;
import com.sap.cloudfoundry.client.facade.domain.Staging;
import org.cloudfoundry.multiapps.common.ContentException;
import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters;
import org.cloudfoundry.multiapps.mta.util.PropertiesUtil;
import org.springframework.util.CollectionUtils;

public class StagingParametersParser implements ParametersParser<Staging> {
import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_NOT_ALLOWED_WITH_DOCKER;
import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_REQUIRED_FOR_CNB;
import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE;
import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_REQUIRED;
import static org.cloudfoundry.multiapps.controller.core.Messages.UNSUPPORTED_LIFECYCLE_VALUE;

public class StagingParametersParser implements ParametersParser<Staging> {
private static final String DEFAULT_HEALTH_CHECK_HTTP_ENDPOINT = "/";
private static final String HTTP_HEALTH_CHECK_TYPE = "http";

Expand All @@ -32,6 +40,10 @@ public Staging parse(List<Map<String, Object>> parametersList) {
getDefaultHealthCheckHttpEndpoint(healthCheckType));
Boolean isSshEnabled = (Boolean) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.ENABLE_SSH, null);
DockerInfo dockerInfo = new DockerInfoParser().parse(parametersList);
LifecycleType lifecycleType = parseLifecycleType(parametersList);

validateLifecycleType(lifecycleType, buildpacks, dockerInfo);

return ImmutableStaging.builder()
.command(command)
.buildpacks(buildpacks)
Expand All @@ -42,9 +54,56 @@ public Staging parse(List<Map<String, Object>> parametersList) {
.healthCheckHttpEndpoint(healthCheckHttpEndpoint)
.isSshEnabled(isSshEnabled)
.dockerInfo(dockerInfo)
.lifecycleType(lifecycleType)
.build();
}

private LifecycleType parseLifecycleType(List<Map<String, Object>> parametersList) {
String lifecycleValue = (String) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.LIFECYCLE, null);
if (lifecycleValue == null) {
return null;
}
try {
return LifecycleType.valueOf(lifecycleValue.toUpperCase());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use the method LifecycleType::from ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed

} catch (IllegalArgumentException e) {
throw new ContentException(MessageFormat.format(UNSUPPORTED_LIFECYCLE_VALUE, lifecycleValue));
}
}

private void validateLifecycleType(LifecycleType lifecycleType, List<String> buildpacks, DockerInfo dockerInfo) {
validateBuildpacksWithCNB(lifecycleType, buildpacks);

// Validate Docker-specific conditions
validateDockerInfoWithDocker(lifecycleType, dockerInfo);
validateBuildpacksWithDocker(lifecycleType, buildpacks);
validateDockerInfoWithNonDocker(lifecycleType, dockerInfo);
}

private void validateBuildpacksWithCNB(LifecycleType lifecycleType, List<String> buildpacks) {
if (lifecycleType == LifecycleType.CNB && CollectionUtils.isEmpty(buildpacks)) {
throw new ContentException(BUILDPACKS_REQUIRED_FOR_CNB);
}
}

private void validateDockerInfoWithDocker(LifecycleType lifecycleType, DockerInfo dockerInfo) {
if (lifecycleType == LifecycleType.DOCKER && dockerInfo == null) {
throw new ContentException(DOCKER_INFO_REQUIRED);
}
}

private void validateBuildpacksWithDocker(LifecycleType lifecycleType, List<String> buildpacks) {
if (lifecycleType == LifecycleType.DOCKER && !CollectionUtils.isEmpty(buildpacks)) {
throw new ContentException(BUILDPACKS_NOT_ALLOWED_WITH_DOCKER);
}
}

private void validateDockerInfoWithNonDocker(LifecycleType lifecycleType, DockerInfo dockerInfo) {
if (lifecycleType != LifecycleType.DOCKER && lifecycleType != null && dockerInfo != null) {
throw new ContentException(
MessageFormat.format(DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE, lifecycleType));
}
}

private String getDefaultHealthCheckHttpEndpoint(String healthCheckType) {
return HTTP_HEALTH_CHECK_TYPE.equals(healthCheckType) ? DEFAULT_HEALTH_CHECK_HTTP_ENDPOINT : null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package org.cloudfoundry.multiapps.controller.core.parser;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.sap.cloudfoundry.client.facade.domain.LifecycleType;
import com.sap.cloudfoundry.client.facade.domain.Staging;
import org.cloudfoundry.multiapps.common.ContentException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.util.CollectionUtils;

import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_NOT_ALLOWED_WITH_DOCKER;
import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_REQUIRED_FOR_CNB;
import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE;
import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_REQUIRED;
import static org.cloudfoundry.multiapps.controller.core.Messages.UNSUPPORTED_LIFECYCLE_VALUE;
import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.BUILDPACK;
import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.BUILDPACKS;
import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.DOCKER;
import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.LIFECYCLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class StagingParametersParserTest {

public static final String INVALID_VALUE = "invalid_value";
public static final String CNB = "cnb";
public static final String CUSTOM_BUILDPACK_URL = "custom-buildpack-url";
public static final String IMAGE = "image";
public static final String CLOUDFOUNDRY_TEST_APP = "cloudfoundry/test-app";
public static final String SOME_BUILDPACK = "some-buildpack";
private StagingParametersParser parser;
private List<Map<String, Object>> parametersList;

@BeforeEach
void setup() {
parser = new StagingParametersParser();
parametersList = new ArrayList<>();
}

@Test
void testValidateLifecycleWithCnbAndValidBuildpacks() {
parametersList.add(mapOf(LIFECYCLE, CNB));
parametersList.add(mapOf(BUILDPACKS, List.of(CUSTOM_BUILDPACK_URL)));

Staging staging = parser.parse(parametersList);

assertNotNull(staging);
assertEquals(LifecycleType.CNB, staging.getLifecycleType());
assertNotNull(staging.getBuildpacks());
assertEquals(1, staging.getBuildpacks()
.size());
assertEquals(CUSTOM_BUILDPACK_URL, staging.getBuildpacks()
.get(0));
}

@Test
void testValidateLifecycleWithCnbAndNoBuildpacks() {
parametersList.add(mapOf(LIFECYCLE, CNB));

ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList));
assertEquals(BUILDPACKS_REQUIRED_FOR_CNB, exception.getMessage());
}

@Test
void testValidateLifecycleWithDockerAndValidDockerInfo() {
parametersList.add(getDockerParams());

Staging staging = parser.parse(parametersList);

assertNotNull(staging);
assertEquals(LifecycleType.DOCKER, staging.getLifecycleType());
assertNotNull(staging.getDockerInfo());
}

@Test
void testValidateLifecycleWithDockerAndBuildpacksProvided() {
parametersList.add(getDockerParams());
parametersList.add(mapOf(BUILDPACKS, List.of(SOME_BUILDPACK)));

ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList));
assertEquals(BUILDPACKS_NOT_ALLOWED_WITH_DOCKER, exception.getMessage());
}

@Test
void testValidateLifecycleWithBuildpackAndNoBuildpacks() {
parametersList.add(mapOf(LIFECYCLE, BUILDPACK));

Staging staging = parser.parse(parametersList);

assertNotNull(staging);
assertEquals(LifecycleType.BUILDPACK, staging.getLifecycleType());

List<String> buildpacks = staging.getBuildpacks();
assertTrue(CollectionUtils.isEmpty(buildpacks));
}

@Test
void testValidateLifecycleWithInvalidLifecycleValue() {
parametersList.add(mapOf(LIFECYCLE, INVALID_VALUE));

ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList));
assertEquals(MessageFormat.format(UNSUPPORTED_LIFECYCLE_VALUE, INVALID_VALUE), exception.getMessage());
}

@Test
void testValidateLifecycleWithDockerAndNoDockerInfo() {
parametersList.add(mapOf(LIFECYCLE, DOCKER));

ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList));
assertEquals(DOCKER_INFO_REQUIRED, exception.getMessage());
}

@Test
void testValidateDockerInfoWithNonDockerLifecycle() {
parametersList.add(mapOf(LIFECYCLE, CNB));
parametersList.add(mapOf(BUILDPACKS, List.of(SOME_BUILDPACK)));
parametersList.add(getDockerParams());

ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList));
assertEquals(MessageFormat.format(DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE, LifecycleType.CNB), exception.getMessage());
}

@Test
void testValidateWithAllParametersMissing() {
Staging staging = parser.parse(parametersList);

assertNotNull(staging);
assertNull(staging.getLifecycleType());
assertTrue(CollectionUtils.isEmpty(staging.getBuildpacks()));
assertNull(staging.getDockerInfo());
}

private static Map<String, Object> mapOf(String key, Object value) {
return Collections.singletonMap(key, value);
}

private static Map<String, Object> getDockerParams() {
return Map.of(LIFECYCLE, DOCKER, DOCKER, Map.of(IMAGE, CLOUDFOUNDRY_TEST_APP));
}

}
Loading
Loading