Skip to content

Commit bbf586a

Browse files
authored
Updating stack on upgrade (#864)
1 parent f7fad8c commit bbf586a

File tree

6 files changed

+124
-49
lines changed

6 files changed

+124
-49
lines changed

scripts/acceptance-tests-ide.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
readonly TOOLSMITH_ENV_INPUT="${TOOLSMITH_ENV_INPUT:?must be set}"
6+
readonly DEFAULT_ORG="${DEFAULT_ORG:?must be set}"
7+
readonly DEFAULT_SPACE="${DEFAULT_SPACE:?must be set}"
8+
readonly SKIP_SSL_VALIDATION="${SKIP_SSL_VALIDATION:?must be set}"
9+
10+
declare API_HOST
11+
readonly API_PORT=443
12+
readonly USERNAME="admin"
13+
declare PASSWORD
14+
readonly CLIENT_ID="admin"
15+
declare CLIENT_SECRET
16+
17+
discover_environment() {
18+
local env_name
19+
env_name=$(cat "$TOOLSMITH_ENV_INPUT/name")
20+
21+
eval "$(bbl print-env --metadata-file "$TOOLSMITH_ENV_INPUT/metadata")"
22+
23+
API_HOST="$(jq -r .cf.api_url <"$TOOLSMITH_ENV_INPUT/metadata")"
24+
PASSWORD="$(credhub get -n "/bosh-${env_name}/cf/cf_admin_password" -q)"
25+
CLIENT_SECRET="$(credhub get -n "/bosh-${env_name}/cf/uaa_admin_client_secret" -q)"
26+
}
27+
28+
print_values() {
29+
cat <<EOF
30+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_HOST=${API_HOST}
31+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_API_PORT=${API_PORT}
32+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_USERNAME=${USERNAME}
33+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_PASSWORD=${PASSWORD}
34+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_CLIENT_ID=${CLIENT_ID}
35+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_CLIENT_SECRET=${CLIENT_SECRET}
36+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_ORG=${DEFAULT_ORG}
37+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_DEFAULT_SPACE=${DEFAULT_SPACE}
38+
SPRING_CLOUD_APPBROKER_ACCEPTANCETEST_CLOUDFOUNDRY_SKIP_SSL_VALIDATION=${SKIP_SSL_VALIDATION}
39+
TESTS_BROKERAPPPATH=build/libs/spring-cloud-app-broker-acceptance-tests.jar
40+
EOF
41+
}
42+
43+
main() {
44+
discover_environment
45+
print_values
46+
}
47+
48+
main

spring-cloud-app-broker-acceptance-tests/src/test/java/org/springframework/cloud/appbroker/acceptance/CreateInstanceAcceptanceTest.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@ protected String backingServiceName() {
6464

6565
"spring.cloud.appbroker.services[0].apps[0].environment.ENV_VAR_1=value1",
6666
"spring.cloud.appbroker.services[0].apps[0].environment.ENV_VAR_2=value2",
67-
"spring.cloud.appbroker.services[0].apps[0].properties.stack=cflinuxfs3",
6867
"spring.cloud.appbroker.services[0].apps[0].properties.memory=2G",
6968
"spring.cloud.appbroker.services[0].apps[0].properties.count=2",
7069

7170
"spring.cloud.appbroker.services[0].apps[1].name=" + APP_CREATE_2,
72-
"spring.cloud.appbroker.services[0].apps[1].path=" + BACKING_APP_PATH
71+
"spring.cloud.appbroker.services[0].apps[1].path=" + BACKING_APP_PATH,
72+
73+
"spring.cloud.appbroker.deployer.cloudfoundry.properties.stack=cflinuxfs3"
7374
})
7475
void deployAppsOnCreateService() {
7576
// when a service instance is created
@@ -94,12 +95,6 @@ void deployAppsOnCreateService() {
9495
assertThat(app.getStack()).isEqualTo("cflinuxfs3");
9596
});
9697

97-
// and stack is not updated when not specified
98-
Optional<ApplicationDetail> application2Detail = getApplicationDetail(APP_CREATE_1);
99-
assertThat(application2Detail).hasValueSatisfying(app -> {
100-
assertThat(app.getStack()).isEqualTo("cflinuxfs4");
101-
});
102-
10398
// and has the environment variables
10499
DocumentContext json = getSpringAppJson(APP_CREATE_1);
105100
assertEnvironmentVariablesSet(json);

spring-cloud-app-broker-acceptance-tests/src/test/java/org/springframework/cloud/appbroker/acceptance/UpgradeInstanceAcceptanceTest.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Optional;
2121

2222
import com.jayway.jsonpath.DocumentContext;
23+
import org.cloudfoundry.operations.applications.ApplicationDetail;
2324
import org.cloudfoundry.operations.applications.ApplicationSummary;
2425
import org.junit.jupiter.api.AfterEach;
2526
import org.junit.jupiter.api.BeforeEach;
@@ -78,7 +79,9 @@ protected String backingServiceName() {
7879
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
7980
"spring.cloud.appbroker.services[0].apps[0].path=" + BACKING_APP_PATH,
8081
"spring.cloud.appbroker.services[0].apps[0].environment.parameter1=old-config1",
81-
"spring.cloud.appbroker.services[0].apps[0].environment.parameter2=old-config2"
82+
"spring.cloud.appbroker.services[0].apps[0].environment.parameter2=old-config2",
83+
"spring.cloud.appbroker.services[0].apps[0].parameters-transformers[0].name=PropertyMapping",
84+
"spring.cloud.appbroker.services[0].apps[0].parameters-transformers[0].args.include=upgrade"
8285
})
8386
void createsServiceInstance() {
8487
// when a service instance is created
@@ -107,17 +110,15 @@ void createsServiceInstance() {
107110
"spring.cloud.appbroker.services[0].apps[0].name=" + APP_NAME,
108111
"spring.cloud.appbroker.services[0].apps[0].path=" + BACKING_APP_PATH,
109112
"spring.cloud.appbroker.services[0].apps[0].environment.parameter1=new-config1",
110-
"spring.cloud.appbroker.services[0].apps[0].environment.parameter3=new-config3"
113+
"spring.cloud.appbroker.services[0].apps[0].environment.parameter3=new-config3",
114+
"spring.cloud.appbroker.services[0].apps[0].parameters-transformers[0].name=PropertyMapping",
115+
"spring.cloud.appbroker.services[0].apps[0].parameters-transformers[0].args.include=upgrade",
116+
"spring.cloud.appbroker.deployer.cloudfoundry.properties.stack=cflinuxfs3"
111117
})
112118
void upgradesTheServiceInstanceWithNewBackingServiceAndEnvironmentVariables() {
113119
// when the service instance is updated with a new service
114120
updateServiceInstance(SI_NAME, Collections.singletonMap("upgrade", true));
115121

116-
// then the backing application was updated with zero downtime
117-
healthListener.stop();
118-
assertThat(healthListener.getFailures()).isEqualTo(0);
119-
assertThat(healthListener.getSuccesses()).isGreaterThan(0);
120-
121122
// then a backing application is re-deployed
122123
Optional<ApplicationSummary> updatedBackingApplication = getApplicationSummary(APP_NAME);
123124
assertThat(updatedBackingApplication).hasValueSatisfying(app ->
@@ -129,6 +130,17 @@ void upgradesTheServiceInstanceWithNewBackingServiceAndEnvironmentVariables() {
129130
assertThat(json.read("$.parameter3").toString()).isEqualTo("new-config3");
130131
assertThat(json.jsonString()).doesNotContain("parameter2");
131132

133+
// and stack is updated when specified
134+
Optional<ApplicationDetail> application1Detail = getApplicationDetail(APP_NAME);
135+
assertThat(application1Detail).hasValueSatisfying(app -> {
136+
assertThat(app.getStack()).isEqualTo("cflinuxfs3");
137+
});
138+
139+
// then the backing application was updated with zero downtime
140+
healthListener.stop();
141+
assertThat(healthListener.getFailures()).isEqualTo(0);
142+
assertThat(healthListener.getSuccesses()).isGreaterThan(0);
143+
132144
// when the service instance is deleted
133145
deleteServiceInstance(SI_NAME);
134146

spring-cloud-app-broker-deployer-cloudfoundry/src/main/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployer.java

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
import org.cloudfoundry.client.v2.spaces.CreateSpaceRequest;
5252
import org.cloudfoundry.client.v2.spaces.DeleteSpaceRequest;
5353
import org.cloudfoundry.client.v2.spaces.SpaceEntity;
54+
import org.cloudfoundry.client.v3.BuildpackData;
55+
import org.cloudfoundry.client.v3.Lifecycle;
56+
import org.cloudfoundry.client.v3.LifecycleType;
5457
import org.cloudfoundry.client.v3.Relationship;
5558
import org.cloudfoundry.client.v3.ToOneRelationship;
5659
import org.cloudfoundry.client.v3.applications.ListApplicationPackagesRequest;
@@ -260,7 +263,7 @@ public Mono<UpdateApplicationResponse> preUpdate(UpdateApplicationRequest reques
260263
.properties(request.getProperties())
261264
.build())
262265
.map(GetApplicationResponse::getId)
263-
.flatMap(applicationId -> updateEnvironment(request, applicationId))
266+
.flatMap(applicationId -> updateApplication(request, applicationId))
264267
.thenReturn(UpdateApplicationResponse.builder().name(appName).build());
265268
}
266269

@@ -281,7 +284,7 @@ public Mono<UpdateApplicationResponse> update(UpdateApplicationRequest request)
281284
return associateHostName(applicationId, request.getProperties());
282285
}
283286
})
284-
.flatMap(applicationId -> updateEnvironment(request, applicationId))
287+
.flatMap(applicationId -> updateApplication(request, applicationId))
285288
.flatMap(applicationId -> Mono.zip(Mono.just(applicationId),
286289
upgradeApplicationIfRequired(request, applicationId)))
287290
.flatMap(tuple2 -> {
@@ -580,11 +583,46 @@ private Mono<String> upgradeApplicationIfRequired(UpdateApplicationRequest reque
580583
return getPackageForApplication(applicationId);
581584
}
582585

583-
private Mono<String> updateEnvironment(UpdateApplicationRequest request, String applicationId) {
584-
final Map<String, Object> environmentVariables =
585-
getApplicationEnvironment(request.getProperties(), request.getEnvironment(),
586-
request.getServiceInstanceId());
587-
return updateApplicationEnvironment(applicationId, environmentVariables, request.getProperties())
586+
private Mono<String> updateApplication(UpdateApplicationRequest request, String applicationId) {
587+
final Map<String, Object> environmentVariables = getApplicationEnvironment(request.getProperties(),
588+
request.getEnvironment(), request.getServiceInstanceId());
589+
Map<String, String> properties = request.getProperties();
590+
591+
return updateStackIfPresent(request, applicationId)
592+
.then(this.client.applicationsV2()
593+
.update(org.cloudfoundry.client.v2.applications.UpdateApplicationRequest.builder()
594+
.applicationId(applicationId)
595+
.instances(instances(properties))
596+
.diskQuota(diskQuota(properties))
597+
.memory(memory(properties))
598+
.putAllEnvironmentJsons(environmentVariables)
599+
.build())
600+
.doOnRequest(l -> LOG.debug("Updating environment. applicationId={}", applicationId))
601+
.doOnSuccess(response -> {
602+
LOG.info("Success updating environment. applicationId={}", applicationId);
603+
LOG.debug(RESPONSE_LOG_TEMPLATE, response);
604+
})
605+
.doOnError(e -> LOG.error(String.format("Error updating environment. applicationId=%s, " +
606+
ERROR_LOG_TEMPLATE, applicationId, e.getMessage()), e)))
607+
.thenReturn(applicationId);
608+
}
609+
610+
private Mono<String> updateStackIfPresent(UpdateApplicationRequest request, String applicationId) {
611+
if (!request.getProperties().containsKey("upgrade") ||
612+
!StringUtils.hasText(this.defaultDeploymentProperties.getStack())) {
613+
return Mono.just(applicationId);
614+
}
615+
String stackName = this.defaultDeploymentProperties.getStack();
616+
return this.client.applicationsV3().
617+
update(org.cloudfoundry.client.v3.applications.UpdateApplicationRequest.builder()
618+
.applicationId(applicationId)
619+
.lifecycle(Lifecycle.builder()
620+
.type(LifecycleType.BUILDPACK)
621+
.data(BuildpackData.builder()
622+
.stack(stackName)
623+
.build())
624+
.build())
625+
.build())
588626
.thenReturn(applicationId);
589627
}
590628

@@ -641,27 +679,6 @@ private Mono<CreatePackageResponse> createPackageForApplication(String applicati
641679
applicationId, e.getMessage()), e));
642680
}
643681

644-
private Mono<org.cloudfoundry.client.v2.applications.UpdateApplicationResponse> updateApplicationEnvironment(
645-
String applicationId, Map<String, Object> environmentVariables, Map<String, String> properties) {
646-
return this.client
647-
.applicationsV2()
648-
.update(org.cloudfoundry.client.v2.applications.UpdateApplicationRequest
649-
.builder()
650-
.applicationId(applicationId)
651-
.instances(instances(properties))
652-
.diskQuota(diskQuota(properties))
653-
.memory(memory(properties))
654-
.putAllEnvironmentJsons(environmentVariables)
655-
.build())
656-
.doOnRequest(l -> LOG.debug("Updating environment. applicationId={}", applicationId))
657-
.doOnSuccess(response -> {
658-
LOG.info("Success updating environment. applicationId={}", applicationId);
659-
LOG.debug(RESPONSE_LOG_TEMPLATE, response);
660-
})
661-
.doOnError(e -> LOG.error(String.format("Error updating environment. applicationId=%s, " +
662-
ERROR_LOG_TEMPLATE, applicationId, e.getMessage()), e));
663-
}
664-
665682
private Function<Flux<Long>, Publisher<?>> getExponentialBackOff() {
666683
return DelayUtils.exponentialBackOff(Duration.ofSeconds(2), Duration.ofMinutes(5), Duration.ofMinutes(10));
667684
}

spring-cloud-app-broker-deployer-cloudfoundry/src/test/java/org/springframework/cloud/appbroker/deployer/cloudfoundry/CloudFoundryAppDeployerUpdateApplicationTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.cloudfoundry.client.v3.Lifecycle;
3939
import org.cloudfoundry.client.v3.LifecycleType;
4040
import org.cloudfoundry.client.v3.Relationship;
41+
import org.cloudfoundry.client.v3.applications.ApplicationState;
4142
import org.cloudfoundry.client.v3.applications.ApplicationsV3;
4243
import org.cloudfoundry.client.v3.applications.ListApplicationPackagesResponse;
4344
import org.cloudfoundry.client.v3.builds.BuildState;
@@ -224,6 +225,14 @@ void setUp() {
224225
.resource(createPackage("2019-07-06T10:37:47Z", "package-id-3"))
225226
.resource(createPackage("2019-07-06T11:37:47Z", "package-id-4"))
226227
.build()));
228+
given(applicationsV3.update(any()))
229+
.willReturn(Mono.just(org.cloudfoundry.client.v3.applications.UpdateApplicationResponse.builder()
230+
.createdAt("2019-07-06T11:37:47Z")
231+
.id("id")
232+
.lifecycle(createLifecycle())
233+
.name("app")
234+
.state(ApplicationState.STARTED)
235+
.build()));
227236

228237
given(builds.create(any()))
229238
.willReturn(Mono.just(CreateBuildResponse.builder()

spring-cloud-app-broker-integration-tests/src/test/java/org/springframework/cloud/appbroker/integration/fixtures/CloudControllerStubFixture.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,6 @@ public void stubAppDoesNotExist(final String appName) {
183183
.withBody(cc("empty-query-results"))));
184184
}
185185

186-
public void stubListStacks() {
187-
stubFor(get(urlPathEqualTo("/v2/stacks"))
188-
.willReturn(ok()
189-
.withBody(cc("list-stacks"))));
190-
}
191-
192186
public void stubAppExistsInSpace(final String appName, final String spaceGuid) {
193187
stubFor(get(urlPathEqualTo("/v2/apps/" + appGuid(appName)))
194188
.withMetadata(optionalStubMapping())

0 commit comments

Comments
 (0)