From 70bb83c213214e18397e203cb5e99de3dfbec3dc Mon Sep 17 00:00:00 2001 From: "Adam J. Weigold" Date: Wed, 3 Oct 2018 01:33:47 -0500 Subject: [PATCH] Updating to latest Deployer SPI - updating Marathon client library - adding new option to deploy all apps under one parent group - adding new option to configure forcing group deletion due to marathon client upgrade - fixed various code analysis warnings --- pom.xml | 62 +++++++------------ .../mesos/marathon/MarathonAppDeployer.java | 37 ++++++----- .../MarathonAppDeployerProperties.java | 28 +++++++++ .../marathon/MarathonAppInstanceStatus.java | 10 +-- .../ChronosTaskLauncherIntegrationTests.java | 7 +-- .../spi/mesos/chronos/ChronosTestSupport.java | 8 +-- .../MesosAppDeployerIntegrationTests.java | 4 +- .../chronos/client/ChronosClientTests.java | 5 +- 8 files changed, 89 insertions(+), 72 deletions(-) diff --git a/pom.xml b/pom.xml index fe449f7..459e2a7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 spring-cloud-deployer-mesos - 1.1.0.BUILD-SNAPSHOT + 1.3.0.BUILD-SNAPSHOT org.springframework.cloud jar @@ -13,19 +13,22 @@ org.springframework.cloud spring-cloud-deployer-parent - 1.1.1.RELEASE + 1.3.3.RELEASE 1.8 - 0.4.14 - 1.2.0.BUILD-SNAPSHOT + 0.6.0 + 1.3.3.RELEASE + 1.3.3.RELEASE + 9.5.1 - org.springframework - spring-context + org.springframework.cloud + spring-cloud-context + ${spring-cloud.version} org.springframework.cloud @@ -37,41 +40,31 @@ spring-cloud-deployer-resource-docker ${spring-cloud-deployer-spi.version} - - org.springframework.cloud - spring-cloud-config-client - org.springframework.boot spring-boot-starter-web - com.cloudbees.marathon + com.mesosphere marathon-client ${marathon-client.version} + + + org.slf4j + slf4j-simple + + org.hashids hashids - 1.0.1 + 1.0.3 - - com.netflix.feign + io.github.openfeign feign-core - 8.14.1 - - - com.netflix.feign - feign-gson - 8.14.1 - - - com.google.code.gson - gson - 2.5 + ${feign.version} - org.springframework.cloud spring-cloud-deployer-spi-test @@ -86,22 +79,13 @@ org.springframework.boot spring-boot-starter-test + 2.0.1.RELEASE test - com.github.tomakehurst - wiremock - 2.1.10 - test - - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-webapp + org.springframework.cloud + spring-cloud-contract-wiremock + 2.0.1.RELEASE test diff --git a/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployer.java b/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployer.java index f16e854..7e07f5a 100644 --- a/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployer.java +++ b/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployer.java @@ -17,8 +17,8 @@ package org.springframework.cloud.deployer.spi.mesos.marathon; import java.io.IOException; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -34,7 +34,7 @@ import mesosphere.marathon.client.model.v2.HealthCheck; import mesosphere.marathon.client.model.v2.Port; import mesosphere.marathon.client.model.v2.Task; -import mesosphere.marathon.client.utils.MarathonException; +import mesosphere.marathon.client.MarathonException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -62,9 +62,9 @@ public class MarathonAppDeployer implements AppDeployer { private static final Log logger = LogFactory.getLog(MarathonAppDeployer.class); - private MarathonAppDeployerProperties properties = new MarathonAppDeployerProperties(); + private MarathonAppDeployerProperties properties; - Marathon marathon; + private Marathon marathon; @Autowired public MarathonAppDeployer(MarathonAppDeployerProperties properties, @@ -93,7 +93,7 @@ public String deploy(AppDeploymentRequest request) { int count = (countProperty != null) ? Integer.parseInt(countProperty) : 1; for (int i = 0; i < count; i++) { String instanceId = appId + "/" + request.getDefinition().getName() + "-" + i; - createAppDeployment(request, instanceId, container, Integer.valueOf(i)); + createAppDeployment(request, instanceId, container, i); } } else { @@ -114,8 +114,7 @@ private void createAppDeployment(AppDeploymentRequest request, String deployment app.setContainer(container); app.setId(deploymentId); - Map env = new HashMap<>(); - env.putAll(request.getDefinition().getProperties()); + Map env = new HashMap<>(request.getDefinition().getProperties()); for (String envVar : properties.getEnvironmentVariables()) { String[] strings = envVar.split("=", 2); Assert.isTrue(strings.length == 2, "Invalid environment variable declared: " + envVar); @@ -141,9 +140,9 @@ private void createAppDeployment(AppDeploymentRequest request, String deployment app.setInstances(instances); HealthCheck healthCheck = new HealthCheck(); - healthCheck.setPath("/health"); + healthCheck.setPath("/actuator/health"); healthCheck.setGracePeriodSeconds(300); - app.setHealthChecks(Arrays.asList(healthCheck)); + app.setHealthChecks(Collections.singletonList(healthCheck)); logger.debug("Creating app with definition:\n" + app.toString()); try { @@ -157,7 +156,7 @@ private void createAppDeployment(AppDeploymentRequest request, String deployment private Container createContainer(AppDeploymentRequest request) { Container container = new Container(); Docker docker = new Docker(); - String image = null; + String image; try { image = request.getResource().getURI().getSchemeSpecificPart(); } catch (IOException e) { @@ -165,9 +164,10 @@ private Container createContainer(AppDeploymentRequest request) { } logger.info("Using Docker image: " + image); docker.setImage(image); - Port port = new Port(8080); + Port port = new Port(); + port.setContainerPort(8080); port.setHostPort(0); - docker.setPortMappings(Arrays.asList(port)); + docker.setPortMappings(Collections.singletonList(port)); docker.setNetwork("BRIDGE"); container.setDocker(docker); return container; @@ -235,7 +235,7 @@ private void deleteAppsForGroupDeployment(String groupId) throws MarathonExcepti } if (group.getApps().size() == 0 && group.getGroups().size() == 0) { logger.info(String.format("Deleting group: %s", groupId)); - marathon.deleteGroup(groupId); + marathon.deleteGroup(groupId, properties.isForceGroupDeletion()); } deleteTopLevelGroupForDeployment(groupId); } @@ -250,7 +250,7 @@ private void deleteTopLevelGroupForDeployment(String id) throws MarathonExceptio } if (topGroup.getApps().size() == 0 && topGroup.getGroups().size() == 0) { logger.info(String.format("Deleting group: %s", topLevelGroupId)); - marathon.deleteGroup(topLevelGroupId); + marathon.deleteGroup(topLevelGroupId, properties.isForceGroupDeletion()); } } } @@ -312,11 +312,16 @@ public RuntimeEnvironmentInfo environmentInfo() { private String deduceAppId(AppDeploymentRequest request) { String groupId = request.getDeploymentProperties().get(GROUP_PROPERTY_KEY); String name = request.getDefinition().getName(); + String globalMarathonGroup = properties.getGlobalMarathonGroup(); + String prefix = "/"; + if (StringUtils.hasText(globalMarathonGroup)) { + prefix = "/" + globalMarathonGroup + "/"; + } if (groupId != null) { - return "/" + groupId + "/" + name; + return prefix + groupId + "/" + name; } else { - return "/" + name; + return prefix + name; } } diff --git a/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployerProperties.java b/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployerProperties.java index bc279cc..dcb847a 100644 --- a/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployerProperties.java +++ b/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppDeployerProperties.java @@ -29,6 +29,7 @@ * * @author Eric Bottard * @author Thomas Risberg + * @author Adam J. Weigold */ @ConfigurationProperties(MarathonAppDeployerProperties.PREFIX) public class MarathonAppDeployerProperties { @@ -71,6 +72,17 @@ public class MarathonAppDeployerProperties { */ private List uris = new ArrayList<>(0); + /** + * Whether group deletions should be forced (marathon will not delete the group if there is an ongoing deployment + * within the group if this is set to false). + */ + private boolean forceGroupDeletion = true; + + /** + * Forces all deployed apps to reside underneath a single global group. + */ + private String globalMarathonGroup; + public double getMemory() { return memory; } @@ -126,4 +138,20 @@ public Set getConstraints() { public void setConstraints(Set constraints) { this.constraints = constraints; } + + public boolean isForceGroupDeletion() { + return forceGroupDeletion; + } + + public void setForceGroupDeletion(boolean forceGroupDeletion) { + this.forceGroupDeletion = forceGroupDeletion; + } + + public String getGlobalMarathonGroup() { + return globalMarathonGroup; + } + + public void setGlobalMarathonGroup(String globalMarathonGroup) { + this.globalMarathonGroup = globalMarathonGroup; + } } diff --git a/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppInstanceStatus.java b/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppInstanceStatus.java index e19fd63..d1251b0 100644 --- a/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppInstanceStatus.java +++ b/src/main/java/org/springframework/cloud/deployer/spi/mesos/marathon/MarathonAppInstanceStatus.java @@ -23,10 +23,11 @@ import org.springframework.cloud.deployer.spi.app.AppInstanceStatus; import org.springframework.cloud.deployer.spi.app.DeploymentState; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import mesosphere.marathon.client.model.v2.App; -import mesosphere.marathon.client.model.v2.HealthCheckResult; +import mesosphere.marathon.client.model.v2.HealthCheckResults; import mesosphere.marathon.client.model.v2.Task; /** @@ -78,12 +79,13 @@ public DeploymentState getState() { } } else { - if (app.getInstances().intValue() > app.getTasksRunning().intValue()) { + if (app.getInstances() > app.getTasksRunning()) { return DeploymentState.deploying; } else { - Collection healthCheckResults = task.getHealthCheckResults(); - boolean alive = healthCheckResults != null && healthCheckResults.iterator().next().isAlive(); + Collection healthCheckResults = task.getHealthCheckResults(); + boolean alive = !CollectionUtils.isEmpty(healthCheckResults) && + healthCheckResults.iterator().next().getAlive(); if (!alive && app.getLastTaskFailure() != null) { return DeploymentState.failed; } diff --git a/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTaskLauncherIntegrationTests.java b/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTaskLauncherIntegrationTests.java index 7f4e50a..374ac91 100644 --- a/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTaskLauncherIntegrationTests.java +++ b/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTaskLauncherIntegrationTests.java @@ -19,10 +19,9 @@ import org.junit.ClassRule; import org.junit.Ignore; import org.junit.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.deployer.resource.docker.DockerResource; import org.springframework.cloud.deployer.spi.mesos.MesosAutoConfiguration; import org.springframework.cloud.deployer.spi.mesos.TestConfig; @@ -30,15 +29,13 @@ import org.springframework.cloud.deployer.spi.test.AbstractTaskLauncherIntegrationTests; import org.springframework.cloud.deployer.spi.test.Timeout; import org.springframework.core.io.Resource; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * Integration tests for {@link ChronosTaskLauncher}. * * @author Thomas Risberg */ -@SpringApplicationConfiguration(classes = {TestConfig.class, MesosAutoConfiguration.class}) -@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = {TestConfig.class, MesosAutoConfiguration.class}) public class ChronosTaskLauncherIntegrationTests extends AbstractTaskLauncherIntegrationTests{ @Autowired diff --git a/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTestSupport.java b/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTestSupport.java index 6630ed8..adace8c 100644 --- a/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTestSupport.java +++ b/src/test/java/org/springframework/cloud/deployer/spi/mesos/chronos/ChronosTestSupport.java @@ -16,8 +16,8 @@ package org.springframework.cloud.deployer.spi.mesos.chronos; -import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.deployer.spi.mesos.dcos.DcosClusterProperties; import org.springframework.cloud.deployer.spi.test.junit.AbstractExternalResourceTestSupport; @@ -38,19 +38,19 @@ public class ChronosTestSupport extends AbstractExternalResourceTestSupport