diff --git a/server/src/main/java/com/aws/greengrass/cli/CLIEventStreamAgent.java b/server/src/main/java/com/aws/greengrass/cli/CLIEventStreamAgent.java index d786367..910bf51 100644 --- a/server/src/main/java/com/aws/greengrass/cli/CLIEventStreamAgent.java +++ b/server/src/main/java/com/aws/greengrass/cli/CLIEventStreamAgent.java @@ -9,6 +9,8 @@ import com.aws.greengrass.authorization.Permission; import com.aws.greengrass.authorization.exceptions.AuthorizationException; import com.aws.greengrass.componentmanager.ComponentStore; +import com.aws.greengrass.config.Node; +import com.aws.greengrass.config.Topic; import com.aws.greengrass.config.Topics; import com.aws.greengrass.deployment.DeploymentQueue; import com.aws.greengrass.deployment.model.ConfigurationUpdateOperation; @@ -78,10 +80,12 @@ import java.util.ArrayList; import java.util.Base64; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.inject.Inject; @@ -118,6 +122,7 @@ public class CLIEventStreamAgent { private static final int DEBUG_PASSWORD_LENGTH_REQUIREMENT = 32; private static final String DEBUG_USERNAME = "debug"; private static final Duration DEBUG_PASSWORD_EXPIRATION = Duration.ofHours(8); + private static final int PERSIST_LIMIT = 5; protected static final String CERT_FINGERPRINT_NAMESPACE = "_certificateFingerprint"; @Inject @@ -179,7 +184,29 @@ public void persistLocalDeployment(Topics serviceConfig, Map dep String deploymentId = (String) deploymentDetails.get(DEPLOYMENT_ID_KEY_NAME); Topics localDeploymentDetails = localDeployments.lookupTopics(deploymentId); localDeploymentDetails.replaceAndWait(deploymentDetails); - // TODO: [P41178971]: Implement a limit on no of local deployments to persist status for + // #P39046999 Persist last (5) successful local deployments as part of CLI application code. + if (localDeployments.size() > PERSIST_LIMIT) { + List childrenToRemove = new ArrayList<>(); + AtomicInteger deploymentSucceeded = new AtomicInteger(); + localDeployments.forEach(topics -> { + if (topics instanceof Topics) { + Topics deploymentTopics = (Topics) topics; + String value = Coerce.toString(deploymentTopics.find(DEPLOYMENT_STATUS_KEY_NAME)); + if (DeploymentStatus.SUCCEEDED.toString().equals(value)) { + deploymentSucceeded.getAndIncrement(); + childrenToRemove.add(deploymentTopics); + } + } + }); + // if more than PERSIST_LIMIT deployments are success, remove until PERSIST_LIMIT left + if (deploymentSucceeded.get() > PERSIST_LIMIT) { + childrenToRemove.stream() + .sorted(Comparator.comparingLong(t -> t.find(DEPLOYMENT_STATUS_KEY_NAME) == null ? + 0L : t.find(DEPLOYMENT_STATUS_KEY_NAME).getModtime())) + .limit(childrenToRemove.size() - PERSIST_LIMIT) + .forEach(Node::remove); + } + } } @SuppressFBWarnings("SIC_INNER_SHOULD_BE_STATIC") diff --git a/server/src/test/java/com/aws/greengrass/cli/CLIEventStreamAgentTest.java b/server/src/test/java/com/aws/greengrass/cli/CLIEventStreamAgentTest.java index 9c76ce2..53a700d 100644 --- a/server/src/test/java/com/aws/greengrass/cli/CLIEventStreamAgentTest.java +++ b/server/src/test/java/com/aws/greengrass/cli/CLIEventStreamAgentTest.java @@ -120,8 +120,8 @@ class CLIEventStreamAgentTest { @BeforeEach void setup() throws AuthorizationException { - when(mockContext.getContinuation()).thenReturn(mock(ServerConnectionContinuation.class)); - when(mockContext.getAuthenticationData()).thenReturn(() -> String.format(GREENGRASS_CLI_CLIENT_ID_FMT, "abc")); + lenient().when(mockContext.getContinuation()).thenReturn(mock(ServerConnectionContinuation.class)); + lenient().when(mockContext.getAuthenticationData()).thenReturn(() -> String.format(GREENGRASS_CLI_CLIENT_ID_FMT, "abc")); lenient().when(authorizationHandler.isAuthorized(eq(CLI_SERVICE), any())) .thenThrow(new AuthorizationException("bad")); } @@ -533,4 +533,31 @@ void test_createDebugPassword() throws IOException { assertEquals("ABCDE", response.getCertificateSHA256Hash()); } } + + @Test + void test_persist_last_five_successful_local_deployments() throws InterruptedException { + Context context = new Context(); + Topics cliServiceConfig = Topics.of(context, TEST_SERVICE, null); + + CLIEventStreamAgent.LocalDeploymentDetails localDeploymentDetails = new CLIEventStreamAgent.LocalDeploymentDetails(); + for (int i = 1; i <= 7; i++) { + String deploymentId = "deploymentId" + i; + localDeploymentDetails.setDeploymentId(deploymentId); + localDeploymentDetails.setDeploymentType(Deployment.DeploymentType.LOCAL); + if (i == 3) { + localDeploymentDetails.setStatus(DeploymentStatus.FAILED); + } else { + localDeploymentDetails.setStatus(DeploymentStatus.SUCCEEDED); + } + cliEventStreamAgent.persistLocalDeployment(cliServiceConfig, localDeploymentDetails.convertToMapOfObject()); + // to ensure topic add as order + Thread.sleep(1); + context.waitForPublishQueueToClear(); + } + + Topics localDeployments = cliServiceConfig.findTopics(PERSISTENT_LOCAL_DEPLOYMENTS); + + assertEquals(6, localDeployments.size()); + assertNull(localDeployments.findNode("deploymentId1")); + } }