diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStep.java index 23c67d1c44..8eef8b6ac0 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStep.java @@ -1,8 +1,5 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -11,9 +8,10 @@ import java.util.TreeSet; import java.util.stream.Collectors; +import com.sap.cloudfoundry.client.facade.CloudControllerClient; +import com.sap.cloudfoundry.client.facade.domain.CloudServiceKey; import jakarta.inject.Inject; import jakarta.inject.Named; - import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.controller.api.model.ProcessType; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; @@ -35,6 +33,7 @@ import org.cloudfoundry.multiapps.controller.core.util.CloudModelBuilderUtil; import org.cloudfoundry.multiapps.controller.core.util.NameUtil; import org.cloudfoundry.multiapps.controller.process.Messages; +import org.cloudfoundry.multiapps.controller.process.util.DeprecatedBuildpackChecker; import org.cloudfoundry.multiapps.controller.process.util.ProcessTypeParser; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.cloudfoundry.multiapps.mta.builders.v2.ParametersChainBuilder; @@ -51,8 +50,8 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; -import com.sap.cloudfoundry.client.facade.CloudControllerClient; -import com.sap.cloudfoundry.client.facade.domain.CloudServiceKey; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; @Named("buildCloudDeployModelStep") @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -62,6 +61,8 @@ public class BuildCloudDeployModelStep extends SyncFlowableStep { private ModuleToDeployHelper moduleToDeployHelper; @Inject private ProcessTypeParser processTypeParser; + @Inject + private DeprecatedBuildpackChecker buildpackChecker; @Override protected StepPhase executeStep(ProcessContext context) { @@ -89,6 +90,10 @@ protected StepPhase executeStep(ProcessContext context) { List modulesCalculatedForDeployment = calculateModulesForDeployment(context, deploymentDescriptor, mtaDescriptorModules, mtaManifestModulesNames, deployedModuleNames, mtaModulesForDeployment); + + buildpackChecker.warnForDeprecatedBuildpacks(modulesCalculatedForDeployment, deploymentDescriptor, getStepLogger(), + moduleToDeployHelper); + List moduleJsons = modulesCalculatedForDeployment.stream() .map(SecureSerialization::toJson) .collect(toList()); @@ -179,7 +184,7 @@ private List getAppNames(List modulesCalculatedForDeployment) { .map(NameUtil::getApplicationName) .collect(toList()); } - + private List buildServicesForBindings(ServicesCloudModelBuilder servicesCloudModelBuilder, DeploymentDescriptor deploymentDescriptor, List modulesCalculatedForDeployment) { @@ -191,11 +196,13 @@ private List buildServicesForBindings(ServicesClou private List buildFilteredServices(DeploymentDescriptor deploymentDescriptor, List filteredResourceNames, ServicesCloudModelBuilder servicesCloudModelBuilder) { - CloudModelBuilderContentCalculator resourcesCloudModelBuilderContentCalculator = new ResourcesCloudModelBuilderContentCalculator(filteredResourceNames, - getStepLogger(), - false); + CloudModelBuilderContentCalculator resourcesCloudModelBuilderContentCalculator = new ResourcesCloudModelBuilderContentCalculator( + filteredResourceNames, + getStepLogger(), + false); // this always filters the 'isActive', 'isResourceSpecifiedForDeployment' and 'isService' resources - List calculatedFilteredResources = resourcesCloudModelBuilderContentCalculator.calculateContentForBuilding(deploymentDescriptor.getResources()); + List calculatedFilteredResources = resourcesCloudModelBuilderContentCalculator.calculateContentForBuilding( + deploymentDescriptor.getResources()); return servicesCloudModelBuilder.build(calculatedFilteredResources); } @@ -235,9 +242,10 @@ private List calculateModulesForDeployment(ProcessContext context, Deplo private List calculateResourcesForDeployment(ProcessContext context, DeploymentDescriptor deploymentDescriptor) { List resourcesSpecifiedForDeployment = context.getVariable(Variables.RESOURCES_FOR_DEPLOYMENT); - CloudModelBuilderContentCalculator resourcesCloudModelBuilderContentCalculator = new ResourcesCloudModelBuilderContentCalculator(resourcesSpecifiedForDeployment, - getStepLogger(), - shouldProcessOnlyUserProvidedServices(context)); + CloudModelBuilderContentCalculator resourcesCloudModelBuilderContentCalculator = new ResourcesCloudModelBuilderContentCalculator( + resourcesSpecifiedForDeployment, + getStepLogger(), + shouldProcessOnlyUserProvidedServices(context)); return resourcesCloudModelBuilderContentCalculator.calculateContentForBuilding(deploymentDescriptor.getResources()); } @@ -247,8 +255,8 @@ private boolean shouldProcessOnlyUserProvidedServices(ProcessContext context) { } protected ModulesCloudModelBuilderContentCalculator - getModulesContentCalculator(ProcessContext context, List mtaDescriptorModules, Set mtaManifestModuleNames, - Set deployedModuleNames, Set mtaModuleNamesForDeployment) { + getModulesContentCalculator(ProcessContext context, List mtaDescriptorModules, Set mtaManifestModuleNames, + Set deployedModuleNames, Set mtaModuleNamesForDeployment) { List modulesValidators = getModuleContentValidators(context.getControllerClient(), mtaDescriptorModules, mtaModuleNamesForDeployment, deployedModuleNames); return new ModulesCloudModelBuilderContentCalculator(mtaManifestModuleNames, @@ -306,7 +314,8 @@ private List getDomainsFromApps(ProcessContext context, DeploymentDescri if (!moduleToDeployHelper.isApplication(module)) { continue; } - ParametersChainBuilder parametersChainBuilder = new ParametersChainBuilder(context.getVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR)); + ParametersChainBuilder parametersChainBuilder = new ParametersChainBuilder( + context.getVariable(Variables.COMPLETE_DEPLOYMENT_DESCRIPTOR)); List> parametersList = parametersChainBuilder.buildModuleChain(module.getName()); boolean noRoute = (Boolean) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.NO_ROUTE, false); diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DeprecatedBuildpackChecker.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DeprecatedBuildpackChecker.java new file mode 100644 index 0000000000..1561f1db1b --- /dev/null +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DeprecatedBuildpackChecker.java @@ -0,0 +1,63 @@ +package org.cloudfoundry.multiapps.controller.process.util; + +import java.util.List; +import java.util.Map; + +import jakarta.inject.Named; +import org.cloudfoundry.multiapps.controller.core.helpers.ModuleToDeployHelper; +import org.cloudfoundry.multiapps.controller.core.model.SupportedParameters; +import org.cloudfoundry.multiapps.mta.builders.v2.ParametersChainBuilder; +import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; +import org.cloudfoundry.multiapps.mta.model.Module; +import org.cloudfoundry.multiapps.mta.util.PropertiesUtil; + +@Named +public class DeprecatedBuildpackChecker { + + private static final String DEPRECATION_MESSAGE = "SAP Java Buildpack 1 has been deprecated and is going to be removed from SAP BTP, Cloud Foundry environment after June, 2025!"; + private static final String DEFAULT_BUILDPACK_MESSAGE_1 = "If no buildpack is specified, sap_java_buildpack is currently applied by default for certain module types (java.tomee, java.tomcat, java)."; + private static final String DEFAULT_BUILDPACK_MESSAGE_2 = "This default will change after June - we strongly recommend that you manually migrate to the supported buildpack in advance to avoid deployment issues."; + private static final String DEPRECATION_LINK = "https://help.sap.com/whats-new/cf0cb2cb149647329b5d02aa96303f56?Component=SAP+Java+Buildpack&Valid_as_Of=2025-04-01:2025-04-10&locale=en-US"; + + private static final String DEPRECATED_BUILDPACK = "sap_java_buildpack"; + + public void warnForDeprecatedBuildpacks(List modulesCalculatedForDeployment, + DeploymentDescriptor deploymentDescriptor, StepLogger stepLogger, + ModuleToDeployHelper moduleToDeployHelper) { + List appsWithDeprecatedBuildpacks = modulesCalculatedForDeployment.stream() + .filter(moduleToDeployHelper::isApplication) + .filter(module -> hasDeprecatedBuildpack(module, + deploymentDescriptor + )) + .map(Module::getName) + .toList(); + + if (!appsWithDeprecatedBuildpacks.isEmpty()) { + logDeprecationNotice(stepLogger, appsWithDeprecatedBuildpacks); + } + } + + private boolean hasDeprecatedBuildpack(Module module, DeploymentDescriptor deploymentDescriptor) { + List> parametersList = new ParametersChainBuilder(deploymentDescriptor) + .buildModuleChain(module.getName()); + + List buildpacks = PropertiesUtil.getPluralOrSingular( + parametersList, SupportedParameters.BUILDPACKS, SupportedParameters.BUILDPACK); + + return buildpacks.contains(DEPRECATED_BUILDPACK); + } + + private void logDeprecationNotice(StepLogger stepLogger, List appsWithDeprecatedBuildpacks) { + String separator = "=".repeat(80); + + stepLogger.warn("== ATTENTION: =="); + stepLogger.warn(separator); + stepLogger.warn(DEPRECATION_MESSAGE); + stepLogger.warn(DEFAULT_BUILDPACK_MESSAGE_1); + stepLogger.warn(DEFAULT_BUILDPACK_MESSAGE_2); + stepLogger.warn("Affected modules: " + appsWithDeprecatedBuildpacks); + stepLogger.warn("For more information, see: " + DEPRECATION_LINK); + stepLogger.warn(separator); + } + +} diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStepTest.java index 665af9362b..41fb4bc9d4 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BuildCloudDeployModelStepTest.java @@ -1,16 +1,13 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; +import com.fasterxml.jackson.core.type.TypeReference; +import com.sap.cloudfoundry.client.facade.domain.CloudServiceKey; import org.cloudfoundry.multiapps.common.test.TestUtil; import org.cloudfoundry.multiapps.common.test.Tester.Expectation; import org.cloudfoundry.multiapps.common.util.JsonUtil; @@ -20,6 +17,7 @@ import org.cloudfoundry.multiapps.controller.core.helpers.ModuleToDeployHelper; import org.cloudfoundry.multiapps.controller.core.model.DeployedMta; import org.cloudfoundry.multiapps.controller.core.test.DescriptorTestUtil; +import org.cloudfoundry.multiapps.controller.process.util.DeprecatedBuildpackChecker; import org.cloudfoundry.multiapps.controller.process.util.ProcessTypeParser; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; @@ -29,8 +27,10 @@ import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; -import com.fasterxml.jackson.core.type.TypeReference; -import com.sap.cloudfoundry.client.facade.domain.CloudServiceKey; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; class BuildCloudDeployModelStepTest extends SyncFlowableStepTest { @@ -54,9 +54,12 @@ class BuildCloudDeployModelStepTest extends SyncFlowableStepTest testExecute() { return Stream.of( -// @formatter:off + // @formatter:off Arguments.of(new StepInput("modules-to-deploy-01.json", "services-to-create-01.json", "service-keys-01.json", List.of("api.cf.neo.ondemand.com"), "deployed-mta-12.json")), Arguments.of(new StepInput("modules-to-deploy-01.json", "services-to-create-01.json", "service-keys-01.json", List.of("api.cf.neo.ondemand.com"), null)) // @formatter:on @@ -141,8 +144,8 @@ protected ApplicationCloudModelBuilder getApplicationCloudModelBuilder(ProcessCo @Override protected ModulesCloudModelBuilderContentCalculator - getModulesContentCalculator(ProcessContext context, List mtaDescriptorModules, Set mtaManifestModuleNames, - Set deployedModuleNames, Set allMtaModuleNames) { + getModulesContentCalculator(ProcessContext context, List mtaDescriptorModules, Set mtaManifestModuleNames, + Set deployedModuleNames, Set allMtaModuleNames) { return modulesCloudModelBuilderContentCalculator; }