Skip to content
Closed
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
Expand Up @@ -22,6 +22,11 @@ public abstract class CustomControllerClient {
private String correlationId = StringUtils.EMPTY;
private final CloudControllerHeaderConfiguration headerConfiguration;

protected CustomControllerClient(WebClient webClient, String version) {
this.webClient = webClient;
headerConfiguration = new CloudControllerHeaderConfiguration(version);
}

protected CustomControllerClient(ApplicationConfiguration configuration, WebClientFactory webClientFactory,
CloudCredentials credentials, String correlationID) {
this.webClient = webClientFactory.getWebClient(credentials);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.cloudfoundry.multiapps.controller.core.cf.clients;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.sap.cloudfoundry.client.facade.CloudCredentials;
import com.sap.cloudfoundry.client.facade.domain.ImmutableInstanceInfo;
import com.sap.cloudfoundry.client.facade.domain.ImmutableInstancesInfo;
import com.sap.cloudfoundry.client.facade.domain.InstanceInfo;
import com.sap.cloudfoundry.client.facade.domain.InstanceState;
import com.sap.cloudfoundry.client.facade.domain.InstancesInfo;
import org.cloudfoundry.multiapps.controller.core.util.ApplicationConfiguration;
import org.springframework.web.reactive.function.client.WebClient;

public class CustomInstancesInfoClient extends CustomControllerClient {

private static final String GET_APPLICATION_PROCESS_URL = "/v3/apps/%s/processes/web/stats";

public CustomInstancesInfoClient(WebClient webClient, String version) {
super(webClient, version);
}

public CustomInstancesInfoClient(ApplicationConfiguration configuration,
WebClientFactory webClientFactory, CloudCredentials credentials,
String correlationID) {
super(configuration, webClientFactory, credentials, correlationID);
}

public CustomInstancesInfoClient(ApplicationConfiguration configuration,
WebClientFactory webClientFactory, CloudCredentials credentials) {
super(configuration, webClientFactory, credentials);
}

public InstancesInfo getInstancesInfo(String appGuid) {
return new CustomControllerClientErrorHandler().handleErrorsOrReturnResult(() -> doGetInstancesInfo(appGuid));
}

private InstancesInfo doGetInstancesInfo(String appGuid) {
String url = String.format(GET_APPLICATION_PROCESS_URL, appGuid);
var list = getListOfResources(new InstancesInfoResourceMapper(), url);
return ImmutableInstancesInfo.builder()
.instances(list)
.build();
}

protected static class InstancesInfoResourceMapper extends ResourcesResponseMapper<InstanceInfo> {

@Override
public List<InstanceInfo> getMappedResources() {
return getQueriedResources().stream()
.map(this::buildInstancesInfo)
.collect(Collectors.toList());
}

private InstanceInfo buildInstancesInfo(Map<String, Object> resource) {
return ImmutableInstanceInfo.builder()
.index(Integer.parseInt(resource.get("index")
.toString()))
.state(InstanceState.valueOf(resource.get("state")
.toString()
.toUpperCase()))
.build();

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

import com.sap.cloudfoundry.client.facade.CloudControllerClient;
import com.sap.cloudfoundry.client.facade.CloudCredentials;
import com.sap.cloudfoundry.client.facade.domain.CloudApplication;
import com.sap.cloudfoundry.client.facade.domain.CloudPackage;
import com.sap.cloudfoundry.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo;
import jakarta.inject.Inject;
import jakarta.inject.Named;

import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended;
import org.cloudfoundry.multiapps.controller.client.lib.domain.RestartParameters;
import org.cloudfoundry.multiapps.controller.core.cf.apps.ActionCalculator;
Expand All @@ -16,30 +19,43 @@
import org.cloudfoundry.multiapps.controller.core.cf.apps.ApplicationStateAction;
import org.cloudfoundry.multiapps.controller.core.cf.apps.ChangedApplicationActionCalculator;
import org.cloudfoundry.multiapps.controller.core.cf.apps.UnchangedApplicationActionCalculator;
import org.cloudfoundry.multiapps.controller.core.cf.clients.CustomInstancesInfoClient;
import org.cloudfoundry.multiapps.controller.core.cf.clients.WebClientFactory;
import org.cloudfoundry.multiapps.controller.core.model.Phase;
import org.cloudfoundry.multiapps.controller.core.security.token.TokenService;
import org.cloudfoundry.multiapps.controller.process.Messages;
import org.cloudfoundry.multiapps.controller.process.variables.Variables;
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.CloudApplication;
import com.sap.cloudfoundry.client.facade.domain.CloudPackage;

@Named("determineDesiredStateAchievingActionsStep")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class DetermineDesiredStateAchievingActionsStep extends SyncFlowableStep {

@Inject
private ApplicationStartupStateCalculator startupStateCalculator;
@Inject
private TokenService tokenService;
@Inject
private WebClientFactory webClientFactory;

@Override
protected StepPhase executeStep(ProcessContext context) {
String appName = context.getVariable(Variables.APP_TO_PROCESS)
.getName();
CloudControllerClient client = context.getControllerClient();
CloudApplication app = client.getApplication(appName);
var appInstances = client.getApplicationInstances(app);

OAuth2AccessTokenWithAdditionalInfo token = tokenService.getToken(context.getVariable(Variables.USER),
context.getVariable(Variables.USER_GUID));
CloudCredentials credentials = new CloudCredentials(token);
CustomInstancesInfoClient customInstancesInfoClient = new CustomInstancesInfoClient(configuration, webClientFactory,
credentials, context.getVariable(
Variables.CORRELATION_ID));

var appInstances = customInstancesInfoClient.getInstancesInfo(app.getMetadata()
.getGuid()
.toString());
var appEnv = client.getApplicationEnvironment(app.getGuid());

ApplicationStartupState currentState = startupStateCalculator.computeCurrentState(app, appInstances, appEnv);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package org.cloudfoundry.multiapps.controller.process.steps;

import static org.cloudfoundry.multiapps.controller.process.steps.StepsUtil.disableAutoscaling;
import static org.cloudfoundry.multiapps.controller.process.steps.StepsUtil.enableAutoscaling;

import java.text.MessageFormat;
import java.time.Duration;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import com.sap.cloudfoundry.client.facade.CloudControllerClient;
import com.sap.cloudfoundry.client.facade.CloudCredentials;
import com.sap.cloudfoundry.client.facade.domain.CloudApplication;
import com.sap.cloudfoundry.client.facade.domain.InstanceInfo;
import com.sap.cloudfoundry.client.facade.domain.InstanceState;
import com.sap.cloudfoundry.client.facade.oauth2.OAuth2AccessTokenWithAdditionalInfo;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended;
import org.cloudfoundry.multiapps.controller.core.cf.CloudControllerClientFactory;
import org.cloudfoundry.multiapps.controller.core.cf.clients.CustomInstancesInfoClient;
import org.cloudfoundry.multiapps.controller.core.cf.clients.WebClientFactory;
import org.cloudfoundry.multiapps.controller.core.model.DeployedMta;
import org.cloudfoundry.multiapps.controller.core.model.DeployedMtaApplication;
import org.cloudfoundry.multiapps.controller.core.model.ImmutableIncrementalAppInstanceUpdateConfiguration;
Expand All @@ -22,14 +28,8 @@
import org.slf4j.LoggerFactory;
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.CloudApplication;
import com.sap.cloudfoundry.client.facade.domain.InstanceInfo;
import com.sap.cloudfoundry.client.facade.domain.InstanceState;

import jakarta.inject.Inject;
import jakarta.inject.Named;
import static org.cloudfoundry.multiapps.controller.process.steps.StepsUtil.disableAutoscaling;
import static org.cloudfoundry.multiapps.controller.process.steps.StepsUtil.enableAutoscaling;

@Named("incrementalAppInstancesUpdateStep")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
Expand All @@ -40,11 +40,14 @@ public class IncrementalAppInstancesUpdateStep extends TimeoutAsyncFlowableStep

private final CloudControllerClientFactory clientFactory;
private final TokenService tokenService;
private final WebClientFactory webClientFactory;

@Inject
public IncrementalAppInstancesUpdateStep(CloudControllerClientFactory clientFactory, TokenService tokenService) {
public IncrementalAppInstancesUpdateStep(CloudControllerClientFactory clientFactory, TokenService tokenService,
WebClientFactory webClientFactory) {
this.clientFactory = clientFactory;
this.tokenService = tokenService;
this.webClientFactory = webClientFactory;
}

@Override
Expand All @@ -60,18 +63,29 @@ protected StepPhase executeAsyncStep(ProcessContext context) throws Exception {
disableAutoscaling(context, client, oldApplicationGuid);

UUID applicationId = client.getApplicationGuid(application.getName());
checkWhetherLiveAppNeedsPolling(context, client, oldApplication);
OAuth2AccessTokenWithAdditionalInfo token = tokenService.getToken(context.getVariable(Variables.USER),
context.getVariable(Variables.USER_GUID));
CloudCredentials credentials = new CloudCredentials(token);
CustomInstancesInfoClient customInstancesInfoClient = new CustomInstancesInfoClient(configuration, webClientFactory,
credentials, context.getVariable(
Variables.CORRELATION_ID));

checkWhetherLiveAppNeedsPolling(context, customInstancesInfoClient, oldApplication);
context.getStepLogger()
.info(Messages.STARTING_INCREMENTAL_APPLICATION_INSTANCE_UPDATE_FOR_0, application.getName());
List<InstanceInfo> idleApplicationInstances = client.getApplicationInstances(applicationId)
.getInstances();
List<InstanceInfo> idleApplicationInstances = customInstancesInfoClient.getInstancesInfo(applicationId.toString())
.getInstances();
var incrementalAppInstanceUpdateConfigurationBuilder = ImmutableIncrementalAppInstanceUpdateConfiguration.builder()
.newApplication(application)
.newApplicationInstanceCount(idleApplicationInstances.size());

int oldApplicationInstanceCount = client.getApplicationInstances(oldApplication)
.getInstances()
.size();
.newApplication(
application)
.newApplicationInstanceCount(
idleApplicationInstances.size());

int oldApplicationInstanceCount = customInstancesInfoClient.getInstancesInfo(oldApplication.getMetadata()
.getGuid()
.toString())
.getInstances()
.size();
incrementalAppInstanceUpdateConfigurationBuilder.oldApplication(oldApplication)
.oldApplicationInitialInstanceCount(oldApplicationInstanceCount)
.oldApplicationInstanceCount(oldApplicationInstanceCount);
Expand All @@ -95,7 +109,8 @@ private StepPhase scaleUpNewAppToTheRequiredInstances(ProcessContext context, Cl
client.updateApplicationInstances(application.getName(), application.getInstances());
incrementalAppInstanceUpdateConfigurationBuilder = ImmutableIncrementalAppInstanceUpdateConfiguration.builder()
.newApplication(application)
.newApplicationInstanceCount(application.getInstances());
.newApplicationInstanceCount(
application.getInstances());
context.setVariable(Variables.INCREMENTAL_APP_INSTANCE_UPDATE_CONFIGURATION,
incrementalAppInstanceUpdateConfigurationBuilder.build());
setExecutionIndexForPollingNewAppInstances(context);
Expand All @@ -115,7 +130,8 @@ private DeployedMtaApplication getOldApplication(ProcessContext context, CloudAp
DeployedMtaApplication deployedMtaApplication = deployedMta.getApplications()
.stream()
.filter(deployedApplication -> deployedApplication.getModuleName()
.equals(currentApplication.getModuleName()))
.equals(
currentApplication.getModuleName()))
.findFirst()
.orElse(null);
if (deployedMtaApplication == null) {
Expand All @@ -131,9 +147,12 @@ private DeployedMtaApplication getOldApplication(ProcessContext context, CloudAp
return deployedMtaApplication;
}

private void checkWhetherLiveAppNeedsPolling(ProcessContext context, CloudControllerClient client, CloudApplication cloudApplication) {
private void checkWhetherLiveAppNeedsPolling(ProcessContext context, CustomInstancesInfoClient client,
CloudApplication cloudApplication) {
setExecutionIndexToTriggerNewApplicationRollingUpdate(context);
List<InstanceInfo> appInstances = client.getApplicationInstances(cloudApplication)
List<InstanceInfo> appInstances = client.getInstancesInfo(cloudApplication.getMetadata()
.getGuid()
.toString())
.getInstances();
if (!appInstances.stream()
.allMatch(instanceInfo -> instanceInfo.getState() == InstanceState.RUNNING)) {
Expand Down Expand Up @@ -172,8 +191,8 @@ private void setExecutionIndexToTriggerNewApplicationRollingUpdate(ProcessContex
protected List<AsyncExecution> getAsyncStepExecutions(ProcessContext context) {
// The sequence of executions is crucial, as the incremental blue-green deployment alternates between them during the polling
// process
return List.of(new PollStartLiveAppExecution(clientFactory, tokenService),
new PollStartAppExecutionWithRollbackExecution(clientFactory, tokenService),
return List.of(new PollStartLiveAppExecution(clientFactory, tokenService, configuration, webClientFactory),
new PollStartAppExecutionWithRollbackExecution(clientFactory, tokenService, configuration, webClientFactory),
new PollIncrementalAppInstanceUpdateExecution());
}

Expand Down
Loading
Loading