Skip to content

Commit 8141aea

Browse files
committed
Pre-update of backing applications
The update service flow is now: 1. pre-update backing applications 2. update backing services 3. update backing applications The Cloud Foundry implementation has a pre-update step which applies backing application environment updates. This allows the following backing services update step to read the updated app environment, which is required for correct operation of some services where a rebind is performed on update. The final update backing applications step performs a rolling redeployment as before to ensure both environment changes and any credential updates resulting from a rebind are propagated to the backing apps. #396
1 parent 044c384 commit 8141aea

File tree

10 files changed

+571
-270
lines changed

10 files changed

+571
-270
lines changed

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/deployer/BackingAppDeploymentService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ public interface BackingAppDeploymentService {
3535
*/
3636
Flux<String> deploy(List<BackingApplication> backingApps, String serviceInstanceId);
3737

38+
/**
39+
* Performs any steps necessary prior to backing application and backing service updates
40+
*
41+
* @param backingApps a collection of backing applications
42+
* @param serviceInstanceId the service instance ID
43+
* @return a set of strings, where each corresponds to an application. e.g. the application name
44+
*/
45+
default Flux<String> prepareForUpdate(List<BackingApplication> backingApps, String serviceInstanceId) {
46+
return Flux.empty();
47+
}
48+
3849
/**
3950
* Update the backing applications and associate with the service instance
4051
*

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/deployer/DefaultBackingAppDeploymentService.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,27 @@ public Flux<String> deploy(List<BackingApplication> backingApps, String serviceI
5656
});
5757
}
5858

59+
@Override
60+
public Flux<String> prepareForUpdate(List<BackingApplication> backingApps, String serviceInstanceId) {
61+
return Flux.fromIterable(backingApps)
62+
.parallel()
63+
.runOn(Schedulers.parallel())
64+
.flatMap(backingApplication -> deployerClient.preUpdate(backingApplication, serviceInstanceId))
65+
.sequential()
66+
.doOnRequest(l -> {
67+
LOG.info("Preparing applications for update");
68+
LOG.debug(BACKINGAPPS_LOG_TEMPLATE, backingApps);
69+
})
70+
.doOnComplete(() -> {
71+
LOG.info("Finish preparing applications for update");
72+
LOG.debug(BACKINGAPPS_LOG_TEMPLATE, backingApps);
73+
})
74+
.doOnError(e -> {
75+
LOG.error(String.format("Error preparing applications for update. error=%s", e.getMessage()), e);
76+
LOG.debug(BACKINGAPPS_LOG_TEMPLATE, backingApps);
77+
});
78+
}
79+
5980
@Override
6081
public Flux<String> update(List<BackingApplication> backingApps, String serviceInstanceId) {
6182
return Flux.fromIterable(backingApps)

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/deployer/DeployerClient.java

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ public class DeployerClient {
2626

2727
private static final Logger LOG = Loggers.getLogger(DeployerClient.class);
2828

29-
private static final String BACKINGAPP_LOG_TEMPLATE = "backingApp={}";
30-
31-
private static final String BACKINGSERVICE_LOG_TEMPLATE = "backingService={}";
32-
3329
private final AppDeployer appDeployer;
3430

3531
public DeployerClient(AppDeployer appDeployer) {
@@ -51,45 +47,53 @@ public Mono<String> deploy(BackingApplication backingApplication, String service
5147
.build())
5248
.doOnRequest(l -> {
5349
LOG.info("Deploying application. backingAppName={}", backingApplication.getName());
54-
LOG.debug(BACKINGAPP_LOG_TEMPLATE, backingApplication);
50+
debugLog(backingApplication);
5551
})
5652
.doOnSuccess(response -> {
5753
LOG.info("Success deploying application. backingAppName={}", backingApplication.getName());
58-
LOG.debug("response={}, backingApp={}", response, backingApplication);
54+
debugLog(response, backingApplication);
5955
})
6056
.doOnError(e -> {
6157
LOG.error(String.format("Error deploying application. backingAppName=%s, error=%s",
6258
backingApplication.getName(), e.getMessage()), e);
63-
LOG.debug(BACKINGAPP_LOG_TEMPLATE, backingApplication);
59+
debugLog(backingApplication);
6460
})
6561
.map(DeployApplicationResponse::getName);
6662
}
6763

64+
public Mono<String> preUpdate(BackingApplication backingApplication, String serviceInstanceId) {
65+
return appDeployer.preUpdate(getUpdateApplicationRequest(backingApplication, serviceInstanceId))
66+
.doOnRequest(l -> {
67+
LOG.info("Pre-updating application. backingAppName={}", backingApplication.getName());
68+
debugLog(backingApplication);
69+
})
70+
.doOnSuccess(response -> {
71+
LOG.info("Success pre-updating application. backingAppName={}", backingApplication.getName());
72+
debugLog(response, backingApplication);
73+
})
74+
.doOnError(e -> {
75+
LOG.error(String.format("Error pre-updating application. backingAppName=%s, error=%s",
76+
backingApplication.getName(), e.getMessage()), e);
77+
debugLog(backingApplication);
78+
})
79+
.map(UpdateApplicationResponse::getName);
80+
}
81+
6882
public Mono<String> update(BackingApplication backingApplication, String serviceInstanceId) {
6983
return appDeployer
70-
.update(UpdateApplicationRequest
71-
.builder()
72-
.name(backingApplication.getName())
73-
.path(backingApplication.getPath())
74-
.properties(backingApplication.getProperties())
75-
.environment(backingApplication.getEnvironment())
76-
.services(backingApplication.getServices().stream()
77-
.map(ServicesSpec::getServiceInstanceName)
78-
.collect(Collectors.toList()))
79-
.serviceInstanceId(serviceInstanceId)
80-
.build())
84+
.update(getUpdateApplicationRequest(backingApplication, serviceInstanceId))
8185
.doOnRequest(l -> {
8286
LOG.info("Updating application. backingAppName={}", backingApplication.getName());
83-
LOG.debug(BACKINGAPP_LOG_TEMPLATE, backingApplication);
87+
debugLog(backingApplication);
8488
})
8589
.doOnSuccess(response -> {
8690
LOG.info("Success updating application. backingAppName={}", backingApplication.getName());
87-
LOG.debug("response={}, backingApp={}", response, backingApplication);
91+
debugLog(response, backingApplication);
8892
})
8993
.doOnError(e -> {
9094
LOG.error(String.format("Error updating application. backingAppName=%s, error=%s",
9195
backingApplication.getName(), e.getMessage()), e);
92-
LOG.debug(BACKINGAPP_LOG_TEMPLATE, backingApplication);
96+
debugLog(backingApplication);
9397
})
9498
.map(UpdateApplicationResponse::getName);
9599
}
@@ -103,16 +107,16 @@ public Mono<String> undeploy(BackingApplication backingApplication) {
103107
.build())
104108
.doOnRequest(l -> {
105109
LOG.info("Undeploying application. backingAppName={}", backingApplication.getName());
106-
LOG.debug(BACKINGAPP_LOG_TEMPLATE, backingApplication);
110+
debugLog(backingApplication);
107111
})
108112
.doOnSuccess(response -> {
109113
LOG.info("Success undeploying application. backingAppName={}", backingApplication.getName());
110-
LOG.debug("response={}, backingApp={}", response, backingApplication);
114+
debugLog(response, backingApplication);
111115
})
112116
.doOnError(e -> {
113117
LOG.error(String.format("Error undeploying application. backingAppName=%s, error=%s",
114118
backingApplication.getName(), e.getMessage()), e);
115-
LOG.debug(BACKINGAPP_LOG_TEMPLATE, backingApplication);
119+
debugLog(backingApplication);
116120
})
117121
.onErrorReturn(UndeployApplicationResponse.builder()
118122
.name(backingApplication.getName())
@@ -133,16 +137,16 @@ public Mono<String> createServiceInstance(BackingService backingService) {
133137
.build())
134138
.doOnRequest(l -> {
135139
LOG.info("Creating backing service {}", backingService.getName());
136-
LOG.debug(BACKINGSERVICE_LOG_TEMPLATE, backingService);
140+
debugLog(backingService);
137141
})
138142
.doOnSuccess(response -> {
139143
LOG.info("Success creating backing service {}", backingService.getName());
140-
LOG.debug("response={}, backingService={}", response, backingService);
144+
debugLog(response, backingService);
141145
})
142146
.doOnError(e -> {
143147
LOG.error(String.format("Error creating backing service. backingServiceName=%s, error=%s",
144148
backingService.getName(), e.getMessage()), e);
145-
LOG.debug(BACKINGSERVICE_LOG_TEMPLATE, backingService);
149+
debugLog(backingService);
146150
})
147151
.map(CreateServiceInstanceResponse::getName);
148152
}
@@ -159,16 +163,16 @@ public Mono<String> updateServiceInstance(BackingService backingService) {
159163
.build())
160164
.doOnRequest(l -> {
161165
LOG.info("Updating backing service {}", backingService.getName());
162-
LOG.debug(BACKINGSERVICE_LOG_TEMPLATE, backingService);
166+
debugLog(backingService);
163167
})
164168
.doOnSuccess(response -> {
165169
LOG.info("Success updating backing service {}", backingService.getName());
166-
LOG.debug("response={}, backingService={}", response, backingService);
170+
debugLog(response, backingService);
167171
})
168172
.doOnError(e -> {
169173
LOG.error(String.format("Error updating backing service. backingServiceName=%s, error=%s",
170174
backingService.getName(), e.getMessage()), e);
171-
LOG.debug(BACKINGSERVICE_LOG_TEMPLATE, backingService);
175+
debugLog(backingService);
172176
})
173177
.map(UpdateServiceInstanceResponse::getName);
174178
}
@@ -183,21 +187,51 @@ public Mono<String> deleteServiceInstance(BackingService backingService) {
183187
.build())
184188
.doOnRequest(l -> {
185189
LOG.info("Deleting backing service {}", backingService.getName());
186-
LOG.debug(BACKINGSERVICE_LOG_TEMPLATE, backingService);
190+
debugLog(backingService);
187191
})
188192
.doOnSuccess(response -> {
189193
LOG.info("Success deleting backing service {}", backingService.getName());
190-
LOG.debug("response={}, backingService={}", response, backingService);
194+
debugLog(response, backingService);
191195
})
192196
.doOnError(e -> {
193197
LOG.error(String.format("Error deleting backing service. backingServiceName=%s, error=%s",
194198
backingService.getName(), e.getMessage()), e);
195-
LOG.debug(BACKINGSERVICE_LOG_TEMPLATE, backingService);
199+
debugLog(backingService);
196200
})
197201
.onErrorReturn(DeleteServiceInstanceResponse.builder()
198202
.name(backingService.getServiceInstanceName())
199203
.build())
200204
.map(DeleteServiceInstanceResponse::getName);
201205
}
202206

207+
private static UpdateApplicationRequest getUpdateApplicationRequest(BackingApplication backingApplication,
208+
String serviceInstanceId) {
209+
return UpdateApplicationRequest
210+
.builder()
211+
.name(backingApplication.getName())
212+
.path(backingApplication.getPath())
213+
.properties(backingApplication.getProperties())
214+
.environment(backingApplication.getEnvironment())
215+
.services(backingApplication.getServices().stream()
216+
.map(ServicesSpec::getServiceInstanceName)
217+
.collect(Collectors.toList()))
218+
.serviceInstanceId(serviceInstanceId)
219+
.build();
220+
}
221+
222+
private static void debugLog(BackingApplication backingApplication) {
223+
LOG.debug("backingApp={}", backingApplication);
224+
}
225+
226+
private static void debugLog(BackingService backingService) {
227+
LOG.debug("backingService={}", backingService);
228+
}
229+
230+
private static void debugLog(Object response, BackingApplication backingApplication) {
231+
LOG.debug("response={}, backingApp={}", response, backingApplication);
232+
}
233+
234+
private static void debugLog(Object response, BackingService backingService) {
235+
LOG.debug("response={}, backingService={}", response, backingService);
236+
}
203237
}

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentUpdateServiceInstanceWorkflow.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,42 @@ public AppDeploymentUpdateServiceInstanceWorkflow(BrokeredServices brokeredServi
8282

8383
@Override
8484
public Mono<Void> update(UpdateServiceInstanceRequest request, UpdateServiceInstanceResponse response) {
85-
return updateBackingServices(request)
86-
.thenMany(updateBackingApplications(request))
85+
return preUpdateBackingApplications(request)
86+
.flatMap(backingApps -> updateBackingServices(request).then(Mono.just(backingApps)))
87+
.flatMapMany(backingApps -> updateBackingApplications(request, backingApps))
8788
.then();
8889
}
8990

91+
private Mono<List<BackingApplication>> preUpdateBackingApplications(UpdateServiceInstanceRequest request) {
92+
return getBackingApplicationsForService(request.getServiceDefinition(), request.getPlan())
93+
.flatMap(backingApps -> getTargetForService(request.getServiceDefinition(), request.getPlan())
94+
.flatMap(targetSpec -> targetService.addToBackingApplications(backingApps, targetSpec,
95+
request.getServiceInstanceId()))
96+
.defaultIfEmpty(backingApps))
97+
.flatMap(backingApps ->
98+
appsParametersTransformationService.transformParameters(backingApps, request.getParameters()))
99+
.flatMap(backingApps -> deploymentService.prepareForUpdate(backingApps, request.getServiceInstanceId())
100+
.then(Mono.just(backingApps)))
101+
.doOnRequest(l -> {
102+
LOG.info("Preparing for update of backing applications. serviceDefinitionName={}, planName={}",
103+
request.getServiceDefinition().getName(), request.getPlan().getName());
104+
LOG.debug(REQUEST_LOG_TEMPLATE, request);
105+
})
106+
.doOnSuccess(backingApplications -> {
107+
LOG.info("Finish preparing for update of backing applications. serviceDefinitionName={}, planName={}",
108+
request.getServiceDefinition().getName(), request.getPlan().getName());
109+
LOG.debug(REQUEST_LOG_TEMPLATE, request);
110+
})
111+
.doOnError(e -> {
112+
if (LOG.isErrorEnabled()) {
113+
LOG.error(String.format("Error preparing for update of backing applications. serviceDefinitionName=%s, " +
114+
"planName=%s, error=%s", request.getServiceDefinition().getName(), request.getPlan().getName(),
115+
e.getMessage()), e);
116+
}
117+
LOG.debug(REQUEST_LOG_TEMPLATE, request);
118+
});
119+
}
120+
90121
private Flux<String> updateBackingServices(UpdateServiceInstanceRequest request) {
91122
return getBackingServicesForService(request.getServiceDefinition(), request.getPlan())
92123
.flatMap(backingServices -> getTargetForService(request.getServiceDefinition(), request.getPlan())
@@ -174,14 +205,9 @@ private Set<String> intersection(Set<String> set1, Set<String> set2) {
174205
return set;
175206
}
176207

177-
private Flux<String> updateBackingApplications(UpdateServiceInstanceRequest request) {
178-
return getBackingApplicationsForService(request.getServiceDefinition(), request.getPlan())
179-
.flatMap(backingApps -> getTargetForService(request.getServiceDefinition(), request.getPlan())
180-
.flatMap(targetSpec -> targetService.addToBackingApplications(backingApps, targetSpec,
181-
request.getServiceInstanceId()))
182-
.defaultIfEmpty(backingApps))
183-
.flatMap(backingApps ->
184-
appsParametersTransformationService.transformParameters(backingApps, request.getParameters()))
208+
private Flux<String> updateBackingApplications(UpdateServiceInstanceRequest request,
209+
List<BackingApplication> preTransformedBackingApplications) {
210+
return Mono.just(preTransformedBackingApplications)
185211
.flatMapMany(backingApps -> deploymentService.update(backingApps, request.getServiceInstanceId()))
186212
.doOnRequest(l -> {
187213
LOG.info("Updating backing applications. serviceDefinitionName={}, planName={}",

spring-cloud-app-broker-core/src/test/java/org/springframework/cloud/appbroker/deployer/DefaultBackingAppDeploymentServiceTest.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ void setUp() {
5555
}
5656

5757
@Test
58-
@SuppressWarnings("UnassignedFluxMonoInstance")
5958
void shouldDeployApplications() {
6059
doReturn(Mono.just("app1"))
6160
.when(deployerClient).deploy(backingApps.get(0), "instance-id");
@@ -75,7 +74,44 @@ void shouldDeployApplications() {
7574
}
7675

7776
@Test
78-
@SuppressWarnings("UnassignedFluxMonoInstance")
77+
void shouldPrepareApplicationsForUpdate() {
78+
doReturn(Mono.just("app1"))
79+
.when(deployerClient).preUpdate(backingApps.get(0), "instance-id");
80+
doReturn(Mono.just("app2"))
81+
.when(deployerClient).preUpdate(backingApps.get(1), "instance-id");
82+
83+
List<String> expectedValues = new ArrayList<>();
84+
expectedValues.add("app1");
85+
expectedValues.add("app2");
86+
87+
StepVerifier.create(backingAppDeploymentService.prepareForUpdate(backingApps, "instance-id"))
88+
// update preparations are run in parallel, so the order of completion is not predictable
89+
// ensure that both expected signals are sent in any order
90+
.expectNextMatches(expectedValues::remove)
91+
.expectNextMatches(expectedValues::remove)
92+
.verifyComplete();
93+
}
94+
95+
@Test
96+
void shouldUpdateApplications() {
97+
doReturn(Mono.just("app1"))
98+
.when(deployerClient).update(backingApps.get(0), "instance-id");
99+
doReturn(Mono.just("app2"))
100+
.when(deployerClient).update(backingApps.get(1), "instance-id");
101+
102+
List<String> expectedValues = new ArrayList<>();
103+
expectedValues.add("app1");
104+
expectedValues.add("app2");
105+
106+
StepVerifier.create(backingAppDeploymentService.update(backingApps, "instance-id"))
107+
// updates are run in parallel, so the order of completion is not predictable
108+
// ensure that both expected signals are sent in any order
109+
.expectNextMatches(expectedValues::remove)
110+
.expectNextMatches(expectedValues::remove)
111+
.verifyComplete();
112+
}
113+
114+
@Test
79115
void shouldUndeployApplications() {
80116
doReturn(Mono.just("deleted1"))
81117
.when(deployerClient).undeploy(backingApps.get(0));

0 commit comments

Comments
 (0)