feat(provider/cf): add option for user to define service creation/deletion timeout
MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/ServiceInstances.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/ServiceInstances.java
@@ -47,7 +47,6 @@ public class ServiceInstances {
private final ServiceInstanceService api;
private final Organizations orgs;
private final Spaces spaces;
- private Duration timeout = Duration.ofSeconds(450);
private Duration pollingInterval = Duration.ofSeconds(10);
public void createServiceBindingsByName(CloudFoundryServerGroup cloudFoundryServerGroup, @Nullable List<String> serviceNames) throws CloudFoundryApiException {
@@ -133,7 +132,7 @@ public class ServiceInstances {
return services.get(0);
}
- public void destroyServiceInstance(CloudFoundrySpace space, String serviceInstanceName) throws CloudFoundryApiException {
+ public void destroyServiceInstance(CloudFoundrySpace space, String serviceInstanceName, Duration timeout) throws CloudFoundryApiException {
Resource<ServiceInstance> serviceInstance = getServiceInstance(space, serviceInstanceName);
String serviceInstanceId = serviceInstance.getMetadata().getGuid();
List<Resource<ServiceBinding>> serviceBindings = collectPageResources("service bindings",
@@ -144,11 +143,12 @@ public class ServiceInstances {
}
safelyCall(() -> api.destroyServiceInstance(serviceInstanceId));
- pollServiceInstanceStatus(serviceInstanceName, serviceInstanceId, DELETE);
+ pollServiceInstanceStatus(serviceInstanceName, serviceInstanceId, DELETE, timeout);
}
public void createServiceInstance(String newServiceInstanceName, String serviceName, String servicePlanName,
- Set<String> tags, Map<String, Object> parameters, CloudFoundrySpace space)
+ Set<String> tags, Map<String, Object> parameters, CloudFoundrySpace space,
+ Duration timeout)
throws CloudFoundryApiException, ResourceNotFoundException {
List<CloudFoundryServicePlan> cloudFoundryServicePlans = findAllServicePlansByServiceName(serviceName);
@@ -190,11 +190,11 @@ public class ServiceInstances {
}
}
- pollServiceInstanceStatus(newServiceInstanceName, guid, type);
+ pollServiceInstanceStatus(newServiceInstanceName, guid, type, timeout);
}
// package-level for testing visibility
- void pollServiceInstanceStatus(String serviceInstanceName, String guid, LastOperation.Type type) {
+ void pollServiceInstanceStatus(String serviceInstanceName, String guid, LastOperation.Type type, Duration timeout) {
RetryConfig retryConfig = RetryConfig.custom()
.waitDuration(pollingInterval)
.maxAttempts((int) (timeout.getSeconds() / pollingInterval.getSeconds()))

ADD
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/AbstractCloudFoundryServiceDescription.java
+/*
+ * Copyright 2018 Pivotal, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.description;
+
+import com.netflix.spinnaker.clouddriver.cloudfoundry.model.CloudFoundrySpace;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.Duration;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public abstract class AbstractCloudFoundryServiceDescription extends AbstractCloudFoundryDescription {
+ private String serviceName;
+ private CloudFoundrySpace space;
+ private long timeout = 450;
+
+ public Duration getTimeout() {
+ return Duration.ofSeconds(timeout);
+ }
+}

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/DeployCloudFoundryServiceDescription.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/DeployCloudFoundryServiceDescription.java
@@ -24,7 +24,7 @@ import java.util.Set;
@Data
@EqualsAndHashCode(callSuper = true)
-public class DeployCloudFoundryServiceDescription extends DestroyCloudFoundryServiceDescription {
+public class DeployCloudFoundryServiceDescription extends AbstractCloudFoundryServiceDescription {
String service;
String servicePlan;
Set<String> tags;

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/DestroyCloudFoundryServiceDescription.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/DestroyCloudFoundryServiceDescription.java
package com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.description;
-import com.netflix.spinnaker.clouddriver.cloudfoundry.model.CloudFoundrySpace;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
-public class DestroyCloudFoundryServiceDescription extends AbstractCloudFoundryDescription {
- String serviceName;
- CloudFoundrySpace space;
+public class DestroyCloudFoundryServiceDescription extends AbstractCloudFoundryServiceDescription {
}

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DeployCloudFoundryServiceAtomicOperation.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DeployCloudFoundryServiceAtomicOperation.java
@@ -45,7 +45,8 @@ public class DeployCloudFoundryServiceAtomicOperation implements AtomicOperation
description.getServicePlan(),
description.getTags(),
description.getParameterMap(),
- description.getSpace());
+ description.getSpace(),
+ description.getTimeout());
task.updateStatus(PHASE, "Created service instance '" + description.getServiceName() + "'");
return null;
}

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DestroyCloudFoundryServiceAtomicOperation.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DestroyCloudFoundryServiceAtomicOperation.java
@@ -39,7 +39,7 @@ public class DestroyCloudFoundryServiceAtomicOperation implements AtomicOperatio
task.updateStatus(PHASE, "Initializing the removal of service instance '" + description.getServiceName() + "' from space " + description.getSpace().getName());
description.getClient()
.getServiceInstances()
- .destroyServiceInstance(description.getSpace(), description.getServiceName());
+ .destroyServiceInstance(description.getSpace(), description.getServiceName(), description.getTimeout());
task.updateStatus(PHASE, "Done removing service instance '" + description.getServiceName() + "'");
return null;
}

MODIFY
clouddriver-cloudfoundry/src/test/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/ServiceInstancesTest.java
clouddriver-cloudfoundry/src/test/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/ServiceInstancesTest.java
@@ -51,7 +51,7 @@ public class ServiceInstancesTest {
CloudFoundryConfigurationProperties configProps = new CloudFoundryConfigurationProperties();
configProps.setAsyncOperationTimeoutSecondsDefault(2);
configProps.setPollingIntervalSeconds(1);
- serviceInstances = new ServiceInstances(serviceInstanceService, orgs, spaces, Duration.ofSeconds(4), Duration.ofSeconds(1));
+ serviceInstances = new ServiceInstances(serviceInstanceService, orgs, spaces, Duration.ofSeconds(1));
Page<Service> serviceMappingPageOne = Page.singleton(new Service().setLabel("service1"), "service-guid");
when(serviceInstanceService.findService(any(), anyListOf(String.class))).thenReturn(serviceMappingPageOne);
@@ -124,7 +124,8 @@ public class ServiceInstancesTest {
"ServicePlan1",
Collections.emptySet(),
null,
- cloudFoundrySpace);
+ cloudFoundrySpace,
+ Duration.ofSeconds(4));
verify(serviceInstanceService, times(1)).createServiceInstance(any());
verify(serviceInstanceService, never()).updateServiceInstance(any(), any());
@@ -144,7 +145,8 @@ public class ServiceInstancesTest {
"ServicePlan1",
Collections.emptySet(),
null,
- cloudFoundrySpace);
+ cloudFoundrySpace,
+ Duration.ofSeconds(4));
verify(serviceInstanceService, times(1)).createServiceInstance(any());
verify(serviceInstanceService, never()).getServiceInstanceById("service-instance-guid");
@@ -164,7 +166,8 @@ public class ServiceInstancesTest {
"servicePlanName",
Collections.emptySet(),
null,
- cloudFoundrySpace);
+ cloudFoundrySpace,
+ Duration.ofSeconds(4));
}
@Test
@@ -185,7 +188,8 @@ public class ServiceInstancesTest {
"ServicePlan1",
Collections.emptySet(),
null,
- cloudFoundrySpace);
+ cloudFoundrySpace,
+ Duration.ofSeconds(4));
verify(serviceInstanceService, times(1)).createServiceInstance(any());
verify(serviceInstanceService, times(1)).updateServiceInstance(any(), any());
@@ -210,7 +214,8 @@ public class ServiceInstancesTest {
"ServicePlan1",
Collections.emptySet(),
null,
- cloudFoundrySpace);
+ cloudFoundrySpace,
+ Duration.ofSeconds(4));
verify(serviceInstanceService, times(1)).createServiceInstance(any());
verify(serviceInstanceService, times(1)).updateServiceInstance(any(), any());
@@ -241,7 +246,8 @@ public class ServiceInstancesTest {
"ServicePlan1",
Collections.emptySet(),
null,
- cloudFoundrySpace);
+ cloudFoundrySpace,
+ Duration.ofSeconds(4));
}
@Test
@@ -312,7 +318,7 @@ public class ServiceInstancesTest {
when(serviceInstanceService.destroyServiceInstance(any())).thenReturn(new Response("url", 202, "reason", Collections.emptyList(), null));
when(serviceInstanceService.getServiceInstanceById(any())).thenReturn(pollingServiceInstanceResource).thenThrow(retrofitErrorNotFound);
- serviceInstances.destroyServiceInstance(cloudFoundrySpace, "newServiceInstanceName");
+ serviceInstances.destroyServiceInstance(cloudFoundrySpace, "newServiceInstanceName", Duration.ofSeconds(4));
verify(serviceInstanceService, times(1)).findAllServiceInstancesBySpaceId(any(), any(), anyListOf(String.class));
verify(serviceInstanceService, times(1)).destroyServiceInstance(any());
@@ -337,7 +343,7 @@ public class ServiceInstancesTest {
when(serviceInstanceService.destroyServiceInstance(any())).thenThrow(destroyFailed);
try {
- serviceInstances.destroyServiceInstance(cloudFoundrySpace, "serviceInstanceName");
+ serviceInstances.destroyServiceInstance(cloudFoundrySpace, "serviceInstanceName", Duration.ofSeconds(4));
fail("Expected CloudFoundryApiException");
} catch(CloudFoundryApiException cfe) {
// expected behavior
@@ -356,7 +362,7 @@ public class ServiceInstancesTest {
.thenReturn(Page.singleton(new ServiceBinding(), "service-binding-guid"));
try {
- serviceInstances.destroyServiceInstance(cloudFoundrySpace, "serviceInstanceName");
+ serviceInstances.destroyServiceInstance(cloudFoundrySpace, "serviceInstanceName", Duration.ofSeconds(4));
fail("Expected CloudFoundryApiException");
} catch(CloudFoundryApiException cfe) {
// expected behavior
@@ -379,7 +385,7 @@ public class ServiceInstancesTest {
when(serviceInstanceService.getServiceInstanceById(any()))
.thenReturn(polledServiceInstanceResource, polledServiceInstanceResource, succeededServiceInstanceResource);
- serviceInstances.pollServiceInstanceStatus("new-service-instance-name", "service-instance-guid", DELETE);
+ serviceInstances.pollServiceInstanceStatus("new-service-instance-name", "service-instance-guid", DELETE, Duration.ofSeconds(4));
verify(serviceInstanceService, times(3)).getServiceInstanceById("service-instance-guid");
}
@@ -393,7 +399,7 @@ public class ServiceInstancesTest {
when(serviceInstanceService.getServiceInstanceById(any())).thenReturn(polledServiceInstanceResource);
try {
- serviceInstances.pollServiceInstanceStatus("service-instance-name", "service-instance-guid", DELETE);
+ serviceInstances.pollServiceInstanceStatus("service-instance-name", "service-instance-guid", DELETE, Duration.ofSeconds(4));
fail("Expected CloudFoundryApiException");
} catch (CloudFoundryApiException cfe) {
assertThat(cfe.getMessage()).contains("Service instance 'service-instance-name' DELETE did not complete");
@@ -415,7 +421,7 @@ public class ServiceInstancesTest {
when(serviceInstanceService.getServiceInstanceById(any())).thenReturn(polledServiceInstanceResource, polledServiceInstanceResource, failedServiceInstanceResource);
try {
- serviceInstances.pollServiceInstanceStatus("service-instance-name", "service-instance-guid", UPDATE);
+ serviceInstances.pollServiceInstanceStatus("service-instance-name", "service-instance-guid", UPDATE, Duration.ofSeconds(4));
fail("Expected CloudFoundryApiException");
} catch (CloudFoundryApiException cfe) {
assertThat(cfe.getMessage()).contains("Service instance 'service-instance-name' UPDATE failed");

feat(provider/cf): add option for user to define service creation/deletion timeout