Skip to content

Commit 02696c4

Browse files
committed
Set target on bound services so deleteServiceInstance can find the right service
Also temporarily disable delete space on deleteServiceInstance Connects to #413
1 parent 8e99069 commit 02696c4

File tree

26 files changed

+1104
-186
lines changed

26 files changed

+1104
-186
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.appbroker.acceptance;
18+
19+
import java.util.Collections;
20+
import java.util.Optional;
21+
22+
import org.cloudfoundry.operations.applications.ApplicationSummary;
23+
import org.cloudfoundry.operations.services.ServiceInstance;
24+
import org.junit.jupiter.api.AfterEach;
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.MethodOrderer;
27+
import org.junit.jupiter.api.Order;
28+
import org.junit.jupiter.api.Tag;
29+
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.api.TestInfo;
31+
import org.junit.jupiter.api.TestMethodOrder;
32+
33+
import org.springframework.beans.factory.annotation.Autowired;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
38+
class UpdateInstanceWithNewServiceAndTargetAcceptanceTest extends CloudFoundryAcceptanceTest {
39+
40+
private static final String APP_NAME = "app-new-service";
41+
42+
private static final int FIRST_TEST = 1;
43+
44+
private static final int SECOND_TEST = 2;
45+
46+
private static final String SI_NAME = "si-new-service";
47+
48+
private static final String OLD_BACKING_SI_NAME = "backing-service-instance-old";
49+
50+
private static final String NEW_BACKING_SI_NAME = "backing-service-instance-new";
51+
52+
private static final String SUFFIX = "update-with-new-services-and-target";
53+
54+
private static final String APP_SERVICE_NAME = "app-service-" + SUFFIX;
55+
56+
private static final String BACKING_SERVICE_NAME = "backing-service-" + SUFFIX;
57+
58+
59+
@Autowired
60+
private HealthListener healthListener;
61+
62+
@Override
63+
protected String testSuffix() {
64+
return SUFFIX;
65+
}
66+
67+
@Override
68+
protected String appServiceName() {
69+
return APP_SERVICE_NAME;
70+
}
71+
72+
@Override
73+
protected String backingServiceName() {
74+
return BACKING_SERVICE_NAME;
75+
}
76+
77+
@Test
78+
@Tag("first")
79+
@Order(FIRST_TEST)
80+
@AppBrokerTestProperties({
81+
"spring.cloud.appbroker.services[0].service-name=" + APP_SERVICE_NAME,
82+
"spring.cloud.appbroker.services[0].plan-name=" + PLAN_NAME,
83+
84+
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
85+
"spring.cloud.appbroker.services[0].apps[0].path=" + BACKING_APP_PATH,
86+
"spring.cloud.appbroker.services[0].apps[0].services[0].service-instance-name=" + OLD_BACKING_SI_NAME,
87+
88+
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
89+
"spring.cloud.appbroker.services[0].services[0].plan=" + PLAN_NAME,
90+
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + OLD_BACKING_SI_NAME,
91+
92+
"spring.cloud.appbroker.services[0].target.name=SpacePerServiceInstance"
93+
})
94+
void weCreateAService() {
95+
// when a service instance is created
96+
createServiceInstance(SI_NAME);
97+
98+
// then a backing application is deployed
99+
String spaceName = getServiceInstanceGuid(SI_NAME);
100+
101+
Optional<ApplicationSummary> backingApplication = getApplicationSummary(APP_NAME, spaceName);
102+
assertThat(backingApplication).hasValueSatisfying(app -> assertThat(app.getRunningInstances()).isEqualTo(1));
103+
104+
// and the services are bound to it
105+
ServiceInstance backingServiceInstance = getBackingServiceInstance(OLD_BACKING_SI_NAME, spaceName);
106+
assertThat(backingServiceInstance.getApplications()).contains(APP_NAME);
107+
108+
String path = backingApplication.get().getUrls().get(0);
109+
healthListener.start(path);
110+
}
111+
112+
@Test
113+
@Order(SECOND_TEST)
114+
@Tag("last")
115+
@AppBrokerTestProperties({
116+
"spring.cloud.appbroker.services[0].service-name=" + APP_SERVICE_NAME,
117+
"spring.cloud.appbroker.services[0].plan-name=" + PLAN_NAME,
118+
119+
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
120+
"spring.cloud.appbroker.services[0].apps[0].path=" + BACKING_APP_PATH,
121+
"spring.cloud.appbroker.services[0].apps[0].services[0].service-instance-name=" + NEW_BACKING_SI_NAME,
122+
123+
"spring.cloud.appbroker.services[0].services[0].name=" + BACKING_SERVICE_NAME,
124+
"spring.cloud.appbroker.services[0].services[0].plan=" + PLAN_NAME,
125+
"spring.cloud.appbroker.services[0].services[0].service-instance-name=" + NEW_BACKING_SI_NAME,
126+
127+
"spring.cloud.appbroker.services[0].target.name=SpacePerServiceInstance"
128+
})
129+
void weUpdateTheServiceInstanceWithANewBackingService() {
130+
// when the service instance is updated with a new service
131+
updateServiceInstance(SI_NAME, Collections.emptyMap());
132+
String spaceName = getServiceInstanceGuid(SI_NAME);
133+
134+
// then the backing application was updated with zero downtime
135+
healthListener.stop();
136+
assertThat(healthListener.getFailures()).isEqualTo(0);
137+
assertThat(healthListener.getSuccesses()).isGreaterThan(0);
138+
139+
// then a backing application is re-deployed
140+
Optional<ApplicationSummary> updatedBackingApplication = getApplicationSummary(APP_NAME, spaceName);
141+
assertThat(updatedBackingApplication).hasValueSatisfying(app ->
142+
assertThat(app.getRunningInstances()).isEqualTo(1));
143+
144+
// and the new backing service is bound to it
145+
ServiceInstance newBackingServiceInstance = getBackingServiceInstance(NEW_BACKING_SI_NAME, spaceName);
146+
assertThat(newBackingServiceInstance.getApplications()).contains(APP_NAME);
147+
148+
// and the old backing service is deleted
149+
assertThat(listServiceInstances()).doesNotContain(OLD_BACKING_SI_NAME);
150+
151+
// then the service instance is deleted
152+
deleteServiceInstance(SI_NAME);
153+
154+
// and the backing service is deleted
155+
assertThat(listServiceInstances()).doesNotContain(NEW_BACKING_SI_NAME);
156+
}
157+
158+
@Override
159+
@BeforeEach
160+
void setUp(TestInfo testInfo, BrokerProperties brokerProperties) {
161+
if (testInfo.getTags().contains("first")) {
162+
super.setUp(testInfo, brokerProperties);
163+
}
164+
else {
165+
setUpForBrokerUpdate(brokerProperties);
166+
}
167+
}
168+
169+
@Override
170+
@AfterEach
171+
public void tearDown(TestInfo testInfo) {
172+
if (testInfo.getTags().contains("last")) {
173+
super.tearDown(testInfo);
174+
}
175+
}
176+
177+
}

spring-cloud-app-broker-autoconfigure/src/main/java/org/springframework/cloud/appbroker/autoconfigure/AppBrokerAutoConfiguration.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,9 +31,11 @@
3131
import org.springframework.cloud.appbroker.deployer.BackingApplication;
3232
import org.springframework.cloud.appbroker.deployer.BackingService;
3333
import org.springframework.cloud.appbroker.deployer.BackingServicesProvisionService;
34+
import org.springframework.cloud.appbroker.deployer.BackingSpaceManagementService;
3435
import org.springframework.cloud.appbroker.deployer.BrokeredServices;
3536
import org.springframework.cloud.appbroker.deployer.DefaultBackingAppDeploymentService;
3637
import org.springframework.cloud.appbroker.deployer.DefaultBackingServicesProvisionService;
38+
import org.springframework.cloud.appbroker.deployer.DefaultBackingSpaceManagementService;
3739
import org.springframework.cloud.appbroker.deployer.DeployerClient;
3840
import org.springframework.cloud.appbroker.extensions.credentials.CredentialGenerator;
3941
import org.springframework.cloud.appbroker.extensions.credentials.CredentialProviderFactory;
@@ -324,6 +326,18 @@ public BackingServicesProvisionService backingServicesProvisionService(DeployerC
324326
return new DefaultBackingServicesProvisionService(deployerClient);
325327
}
326328

329+
/**
330+
* Provide a {@link BackingSpaceManagementService} bean
331+
*
332+
* @param deployerClient the DeployerClient bean
333+
* @return the bean
334+
*/
335+
@Bean
336+
@ConditionalOnMissingBean
337+
public BackingSpaceManagementService backingSpaceProvisionService(DeployerClient deployerClient) {
338+
return new DefaultBackingSpaceManagementService(deployerClient);
339+
}
340+
327341
/**
328342
* Provide a {@link CreateServiceInstanceWorkflow} bean
329343
*
@@ -401,13 +415,15 @@ public DeleteServiceInstanceWorkflow appDeploymentDeleteServiceInstanceWorkflow(
401415
BrokeredServices brokeredServices, BackingAppDeploymentService backingAppDeploymentService,
402416
BackingAppManagementService backingAppManagementService,
403417
BackingServicesProvisionService backingServicesProvisionService,
418+
BackingSpaceManagementService backingSpaceManagementService,
404419
CredentialProviderService credentialProviderService, TargetService targetService) {
405420

406421
return new AppDeploymentDeleteServiceInstanceWorkflow(
407422
brokeredServices,
408423
backingAppDeploymentService,
409424
backingAppManagementService,
410425
backingServicesProvisionService,
426+
backingSpaceManagementService,
411427
credentialProviderService,
412428
targetService
413429
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.appbroker.deployer;
18+
19+
import java.util.List;
20+
21+
import reactor.core.publisher.Flux;
22+
23+
public interface BackingSpaceManagementService {
24+
25+
Flux<String> deleteTargetSpaces(List<String> targetSpaces);
26+
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.appbroker.deployer;
18+
19+
import java.util.List;
20+
21+
import reactor.core.publisher.Flux;
22+
import reactor.core.scheduler.Schedulers;
23+
import reactor.util.Logger;
24+
import reactor.util.Loggers;
25+
26+
public class DefaultBackingSpaceManagementService implements BackingSpaceManagementService {
27+
28+
private static final Logger LOG = Loggers.getLogger(DefaultBackingSpaceManagementService.class);
29+
30+
private static final String BACKINGSPACES_LOG_TEMPLATE = "backingSpaces={}";
31+
32+
private final DeployerClient deployerClient;
33+
34+
public DefaultBackingSpaceManagementService(DeployerClient deployerClient) {
35+
this.deployerClient = deployerClient;
36+
}
37+
38+
@Override
39+
public Flux<String> deleteTargetSpaces(List<String> targetSpaces) {
40+
return Flux.fromIterable(targetSpaces)
41+
.parallel()
42+
.runOn(Schedulers.parallel())
43+
.flatMap(deployerClient::deleteSpace)
44+
.sequential()
45+
.doOnRequest(l -> {
46+
LOG.info("Deleting backing spaces");
47+
LOG.debug(BACKINGSPACES_LOG_TEMPLATE, targetSpaces);
48+
})
49+
.doOnComplete(() -> {
50+
LOG.info("Finish deleting backing spaces");
51+
LOG.debug(BACKINGSPACES_LOG_TEMPLATE, targetSpaces);
52+
})
53+
.doOnError(e -> LOG.error(String.format("Error deleting backing spaces. error=%s", e.getMessage()), e));
54+
}
55+
56+
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -204,6 +204,29 @@ public Mono<String> deleteServiceInstance(BackingService backingService) {
204204
.map(DeleteServiceInstanceResponse::getName);
205205
}
206206

207+
public Mono<String> deleteSpace(String spaceName) {
208+
return appDeployer
209+
.deleteBackingSpace(
210+
DeleteBackingSpaceRequest
211+
.builder()
212+
.name(spaceName)
213+
.build())
214+
.doOnRequest(l -> {
215+
LOG.info("Deleting backing space {}", spaceName);
216+
})
217+
.doOnSuccess(response -> {
218+
LOG.info("Success deleting backing space {}", spaceName);
219+
})
220+
.doOnError(e -> {
221+
LOG.error(String.format("Error deleting backing space. backingSpaceName=%s, error=%s",
222+
spaceName, e.getMessage()), e);
223+
})
224+
.onErrorReturn(DeleteBackingSpaceResponse.builder()
225+
.name(spaceName)
226+
.build())
227+
.map(DeleteBackingSpaceResponse::getName);
228+
}
229+
207230
private static UpdateApplicationRequest getUpdateApplicationRequest(BackingApplication backingApplication,
208231
String serviceInstanceId) {
209232
return UpdateApplicationRequest

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/manager/BackingAppManagementService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors
2+
* Copyright 2002-2021 the original author or authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -275,6 +275,7 @@ public Mono<BackingApplications> getDeployedBackingApplications(String serviceIn
275275
.builder()
276276
.name(response.getName())
277277
.services(services)
278+
.properties(app.getProperties())
278279
.environment(response.getEnvironment())
279280
.build()))
280281
.doOnRequest(l -> {

0 commit comments

Comments
 (0)