feat(cf): add clone SG pipeline stage
MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/Applications.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/Applications.java
@@ -294,7 +294,7 @@ public class Applications {
public void updateProcess(String guid, @Nullable String command, @Nullable String healthCheckType, @Nullable String healthCheckEndpoint) throws CloudFoundryApiException {
final Process.HealthCheck healthCheck = healthCheckType != null ?
new Process.HealthCheck().setType(healthCheckType) : null;
- if (healthCheckEndpoint != null) {
+ if (healthCheckEndpoint != null && !healthCheckEndpoint.isEmpty()) {
healthCheck.setData(new Process.HealthCheckData().setEndpoint(healthCheckEndpoint));
}
safelyCall(() -> api.updateProcess(guid, new UpdateProcess(command, healthCheck)));

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/converters/DeployCloudFoundryServerGroupAtomicOperationConverter.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/converters/DeployCloudFoundryServerGroupAtomicOperationConverter.java
@@ -42,6 +42,7 @@ import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import static io.vavr.API.*;
import static java.util.stream.Collectors.toList;
@@ -69,16 +70,31 @@ public class DeployCloudFoundryServerGroupAtomicOperationConverter extends Abstr
@Override
public DeployCloudFoundryServerGroupDescription convertDescription(Map input) {
DeployCloudFoundryServerGroupDescription converted = getObjectMapper().convertValue(input, DeployCloudFoundryServerGroupDescription.class);
- CloudFoundryCredentials credentials = getCredentialsObject(input.get("credentials").toString());
+ String deployCredentials = Optional.ofNullable(converted.getDestination())
+ .map(DeployCloudFoundryServerGroupDescription.Destination::getAccount)
+ .orElse(input.get("credentials").toString());
+ CloudFoundryCredentials credentials = getCredentialsObject(deployCredentials);
converted.setClient(credentials.getClient());
converted.setAccountName(credentials.getName());
- converted.setSpace(findSpace(converted.getRegion(), converted.getClient())
- .orElseThrow(() -> new IllegalArgumentException("Unable to find space '" + converted.getRegion() + "'.")));
+ String region = Optional.ofNullable(converted.getDestination())
+ .map(DeployCloudFoundryServerGroupDescription.Destination::getRegion)
+ .orElse(converted.getRegion());
+ converted.setSpace(findSpace(region, converted.getClient())
+ .orElseThrow(() -> new IllegalArgumentException("Unable to find space '" + region + "'.")));
Map artifactSource = (Map) input.get("artifact");
- if ("artifact".equals(artifactSource.get("type"))) {
+ if (Optional.ofNullable(converted.getSource()).isPresent()) {
+ CloudFoundryCredentials artifactCredentials = getCredentialsObject(converted.getSource().getAccount());
+ converted.setArtifactCredentials(new PackageArtifactCredentials(artifactCredentials.getClient()));
+
+ Artifact artifact = new Artifact();
+ artifact.setType("package");
+ artifact.setReference(getServerGroupId(converted.getSource().getAsgName(),
+ converted.getSource().getRegion(), artifactCredentials.getClient()));
+ converted.setArtifact(artifact);
+ } else {
ArtifactCredentials artifactCredentials = credentialsRepository.getAllCredentials().stream()
.filter(creds -> creds.getName().equals(artifactSource.get("account")))
.findAny()
@@ -86,15 +102,6 @@ public class DeployCloudFoundryServerGroupAtomicOperationConverter extends Abstr
converted.setArtifact(convertToArtifact(artifactCredentials, artifactSource.get("reference").toString()));
converted.setArtifactCredentials(artifactCredentials);
- } else if ("package".equals(artifactSource.get("type"))) {
- CloudFoundryCredentials artifactCredentials = getCredentialsObject(artifactSource.get("account").toString());
- converted.setArtifactCredentials(new PackageArtifactCredentials(artifactCredentials.getClient()));
-
- Artifact artifact = new Artifact();
- artifact.setType("package");
- artifact.setReference(getServerGroupId(artifactSource.get("serverGroupName").toString(),
- artifactSource.get("region").toString(), artifactCredentials.getClient()));
- converted.setArtifact(artifact);
}
Map manifest = (Map) input.get("manifest");

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/DeployCloudFoundryServerGroupDescription.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/description/DeployCloudFoundryServerGroupDescription.java
@@ -35,9 +35,15 @@ public class DeployCloudFoundryServerGroupDescription extends AbstractCloudFound
private String accountName;
private String application;
private String stack;
- private String detail;
+ private String freeFormDetails;
private CloudFoundrySpace space;
+ @Nullable
+ private Source source;
+
+ @Nullable
+ private Destination destination;
+
@JsonIgnore
private Artifact artifact;
@@ -71,4 +77,17 @@ public class DeployCloudFoundryServerGroupDescription extends AbstractCloudFound
@Nullable
private List<String> services;
}
+
+ @Data
+ public static class Source {
+ String account;
+ String region;
+ String asgName;
+ }
+
+ @Data
+ public static class Destination {
+ String account;
+ String region;
+ }
}

MODIFY
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DeployCloudFoundryServerGroupAtomicOperation.java
clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DeployCloudFoundryServerGroupAtomicOperation.java
@@ -33,6 +33,7 @@ import javax.annotation.Nullable;
import java.io.*;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.function.Function;
import static com.netflix.spinnaker.clouddriver.cloudfoundry.deploy.ops.CloudFoundryOperationUtils.describeProcessState;
@@ -63,7 +64,7 @@ public class DeployCloudFoundryServerGroupAtomicOperation
clusterProvider, description.getSpace());
description.setServerGroupName(serverGroupNameResolver.resolveNextServerGroupName(description.getApplication(),
- description.getStack(), description.getDetail(), false));
+ description.getStack(), description.getFreeFormDetails(), false));
final CloudFoundryServerGroup serverGroup = createApplication(description);
String packageId = buildPackage(serverGroup.getId(), description);
@@ -99,8 +100,11 @@ public class DeployCloudFoundryServerGroupAtomicOperation
private DeploymentResult deploymentResult() {
DeploymentResult deploymentResult = new DeploymentResult();
- deploymentResult.setServerGroupNames(Collections.singletonList(description.getRegion() + ":" + description.getServerGroupName()));
- deploymentResult.getServerGroupNameByRegion().put(description.getRegion(), description.getServerGroupName());
+ String destinationRegion = Optional.ofNullable(description.getDestination())
+ .map(DeployCloudFoundryServerGroupDescription.Destination::getRegion)
+ .orElse(description.getRegion());
+ deploymentResult.setServerGroupNames(Collections.singletonList(destinationRegion + ":" + description.getServerGroupName()));
+ deploymentResult.getServerGroupNameByRegion().put(destinationRegion, description.getServerGroupName());
deploymentResult.setMessages(getTask().getHistory().stream()
.map(hist -> hist.getPhase() + ":" + hist.getStatus())
.collect(toList()));

MODIFY
clouddriver-cloudfoundry/src/test/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/converters/DeployCloudFoundryServerGroupAtomicOperationConverterTest.java
clouddriver-cloudfoundry/src/test/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/converters/DeployCloudFoundryServerGroupAtomicOperationConverterTest.java
@@ -62,16 +62,16 @@ class DeployCloudFoundryServerGroupAtomicOperationConverterTest {
});
}
- return new CloudFoundryCredentials(name, "", "", "", "", "", "")
- {
+ return new CloudFoundryCredentials(name, "", "", "", "", "", "") {
public CloudFoundryClient getClient() {
return cloudFoundryClient;
}
};
}
- private List<String> accounts = List.of("test", "sourceAccount", "destinationAccount");
+ private List<String> accounts = List.of("test", "sourceAccount", "sourceAccount1", "sourceAccount2", "destinationAccount");
private final ArtifactCredentialsRepository artifactCredentialsRepository = new ArtifactCredentialsRepository();
+
{
accounts.toStream().forEach(account -> artifactCredentialsRepository.save(new ArtifactCredentialsFromString(
account,
@@ -81,9 +81,11 @@ class DeployCloudFoundryServerGroupAtomicOperationConverterTest {
}
private final AccountCredentialsRepository accountCredentialsRepository = new MapBackedAccountCredentialsRepository();
+
{
accounts.toStream().forEach(account -> accountCredentialsRepository.update(account, createCredentials(account)));
}
+
private final AccountCredentialsProvider accountCredentialsProvider =
new DefaultAccountCredentialsProvider(accountCredentialsRepository);
@@ -97,14 +99,13 @@ class DeployCloudFoundryServerGroupAtomicOperationConverterTest {
}
@Test
- void convertDescriptionWithPackageArtifactSourceAndDownloadedManifest() {
+ void convertDescriptionWitSourceSet() {
final Map input = HashMap.of(
"credentials", "destinationAccount",
"region", "org > space",
- "artifact", HashMap.of(
- "type", "package",
+ "source", HashMap.of(
"account", "sourceAccount",
- "serverGroupName", "serverGroupName1",
+ "asgName", "serverGroupName1",
"region", "org > space"
).toJavaMap(),
"manifest", HashMap.of(
@@ -132,6 +133,45 @@ class DeployCloudFoundryServerGroupAtomicOperationConverterTest {
);
}
+ @Test
+ void convertDescriptionWithDestinationSet() {
+ final Map input = HashMap.of(
+ "credentials", "destinationAccount",
+ "region", "org > space",
+ "destination", HashMap.of(
+ "account", "sourceAccount1",
+ "region", "org1 > space1"
+ ).toJavaMap(),
+ "source", HashMap.of(
+ "account", "sourceAccount2",
+ "asgName", "serverGroupName1",
+ "region", "org > space"
+ ).toJavaMap(),
+ "manifest", HashMap.of(
+ "type", "artifact",
+ "account", "test",
+ "reference", "ref1"
+ ).toJavaMap()
+ ).toJavaMap();
+
+ final DeployCloudFoundryServerGroupDescription result = converter.convertDescription(input);
+
+ CloudFoundryCredentials sourceCredentials = converter.getCredentialsObject("sourceAccount2");
+ assertThat(result.getAccountName()).isEqualTo("sourceAccount1");
+ assertThat(result.getArtifactCredentials())
+ .isEqualToComparingFieldByFieldRecursively(new PackageArtifactCredentials(sourceCredentials.getClient()));
+ assertThat(result.getSpace()).isEqualToComparingFieldByFieldRecursively(
+ CloudFoundrySpace.builder().id("space1ID").name("space1").organization(
+ CloudFoundryOrganization.builder().id("org1ID").name("org1").build()).build());
+ assertThat(result.getApplicationAttributes()).isEqualToComparingFieldByFieldRecursively(
+ new DeployCloudFoundryServerGroupDescription.ApplicationAttributes()
+ .setInstances(42)
+ .setMemory("1024")
+ .setDiskQuota("1024")
+ .setBuildpacks(Collections.emptyList())
+ );
+ }
+
@Test
void convertManifestMapToApplicationAttributes() {
final Map input = HashMap.of(

MODIFY
clouddriver-cloudfoundry/src/test/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DeployCloudFoundryServerGroupAtomicOperationTest.java
clouddriver-cloudfoundry/src/test/java/com/netflix/spinnaker/clouddriver/cloudfoundry/deploy/ops/DeployCloudFoundryServerGroupAtomicOperationTest.java
@@ -72,7 +72,7 @@ class DeployCloudFoundryServerGroupAtomicOperationTest extends AbstractCloudFoun
.setAccountName("account1")
.setApplication("app1")
.setStack("stack1")
- .setDetail("detail1")
+ .setFreeFormDetails("detail1")
.setArtifactCredentials(new ArtifactCredentialsFromString(
"test",
io.vavr.collection.List.of("a").asJava(),

feat(cf): add clone SG pipeline stage (#3384)