From 9f100aa809abad5b8ced9876ef178f01e9ec500a Mon Sep 17 00:00:00 2001 From: Greg Meldrum Date: Fri, 1 Dec 2023 08:54:26 -0500 Subject: [PATCH] DATAGO-64297: Added message subscriber/publisher (#134) --- .../ep/common/messages/CommandMessage.java | 2 +- .../agent/command/CommandManager.java | 27 ++++- .../agent/command/mapper/CommandMapper.java | 13 +++ .../agent/command/rest/CommandController.java | 28 ----- .../agent/publisher/CommandPublisher.java | 39 +++++++ .../subscriber/CommandMessageHandler.java | 29 +++++ .../subscriber/SolaceMessageHandler.java | 36 +++--- .../ep/event/management/agent/TestConfig.java | 21 ++++ .../commandManager/CommandManagerTests.java | 106 ++++++++++++++++++ service/plugin/pom.xml | 32 ++++++ .../plugin/command/model/CommandResult.java | 1 - .../agent/plugin/mop/MOPProtocol.java | 2 +- .../TerraformLogProcessingService.java | 17 ++- .../terraform/manager/TerraformManager.java | 11 +- .../plugin/terraform/TerraformCommandIT.java | 32 ++++-- .../real/TerraformClientRealTests.java | 2 - 16 files changed, 330 insertions(+), 68 deletions(-) create mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/mapper/CommandMapper.java delete mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/rest/CommandController.java create mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/publisher/CommandPublisher.java create mode 100644 service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/CommandMessageHandler.java create mode 100644 service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java diff --git a/service/application/src/main/java/com/solace/maas/ep/common/messages/CommandMessage.java b/service/application/src/main/java/com/solace/maas/ep/common/messages/CommandMessage.java index 4ca3a5b0d..262efd786 100644 --- a/service/application/src/main/java/com/solace/maas/ep/common/messages/CommandMessage.java +++ b/service/application/src/main/java/com/solace/maas/ep/common/messages/CommandMessage.java @@ -27,7 +27,7 @@ public CommandMessage(String serviceId, List commandBundles) { super(); withMessageType(MOPMessageType.generic) - .withProtocol(MOPProtocol.commandProtocol) + .withProtocol(MOPProtocol.epConfigPush) .withVersion("1") .withUhFlag(MOPUHFlag.ignore); this.serviceId = serviceId; diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java index de5c2a856..c7bee5129 100644 --- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java @@ -1,12 +1,15 @@ package com.solace.maas.ep.event.management.agent.command; +import com.solace.maas.ep.common.messages.CommandMessage; +import com.solace.maas.ep.event.management.agent.command.mapper.CommandMapper; +import com.solace.maas.ep.event.management.agent.config.eventPortal.EventPortalProperties; import com.solace.maas.ep.event.management.agent.plugin.command.model.Command; import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandBundle; -import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandRequest; import com.solace.maas.ep.event.management.agent.plugin.service.MessagingServiceDelegateService; import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SempClient; import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SolaceHttpSemp; import com.solace.maas.ep.event.management.agent.plugin.terraform.manager.TerraformManager; +import com.solace.maas.ep.event.management.agent.publisher.CommandPublisher; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -17,28 +20,42 @@ @Service public class CommandManager { private final TerraformManager terraformManager; - + private final CommandMapper commandMapper; + private final CommandPublisher commandPublisher; private final MessagingServiceDelegateService messagingServiceDelegateService; + private final EventPortalProperties eventPortalProperties; - public CommandManager(TerraformManager terraformManager, MessagingServiceDelegateService messagingServiceDelegateService) { + public CommandManager(TerraformManager terraformManager, CommandMapper commandMapper, + CommandPublisher commandPublisher, MessagingServiceDelegateService messagingServiceDelegateService, + EventPortalProperties eventPortalProperties) { this.terraformManager = terraformManager; + this.commandMapper = commandMapper; + this.commandPublisher = commandPublisher; this.messagingServiceDelegateService = messagingServiceDelegateService; + this.eventPortalProperties = eventPortalProperties; } - public void execute(CommandRequest request) { + public void execute(CommandMessage request) { Map envVars = setBrokerSpecificEnvVars(request.getServiceId()); for (CommandBundle bundle : request.getCommandBundles()) { // For now everything is run serially for (Command command : bundle.getCommands()) { switch (command.getCommandType()) { case terraform: - terraformManager.execute(request, command, envVars); + terraformManager.execute(commandMapper.map(request), command, envVars); break; default: throw new IllegalStateException("Unexpected value: " + command.getCommandType()); } } } + + Map topicVars = Map.of( + "orgId", request.getOrgId(), + "runtimeAgentId", eventPortalProperties.getRuntimeAgentId(), + "correlationId", request.getCorrelationId() + ); + commandPublisher.sendCommandResponse(request, topicVars); } private Map setBrokerSpecificEnvVars(String messagingServiceId) { diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/mapper/CommandMapper.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/mapper/CommandMapper.java new file mode 100644 index 000000000..0328184b0 --- /dev/null +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/mapper/CommandMapper.java @@ -0,0 +1,13 @@ +package com.solace.maas.ep.event.management.agent.command.mapper; + +import com.solace.maas.ep.common.messages.CommandMessage; +import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandRequest; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface CommandMapper { + + CommandRequest map(CommandMessage input); + + CommandMessage map(CommandRequest input); +} \ No newline at end of file diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/rest/CommandController.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/rest/CommandController.java deleted file mode 100644 index bb66907e8..000000000 --- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/rest/CommandController.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.solace.maas.ep.event.management.agent.command.rest; - -import com.solace.maas.ep.event.management.agent.command.CommandManager; -import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandRequest; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@CrossOrigin -@RestController -@RequestMapping("/api/v2/ema/command") - -public class CommandController { - private final CommandManager commandManager; - - public CommandController(CommandManager commandManager) { - this.commandManager = commandManager; - } - - @PostMapping - public ResponseEntity executeTfCommand(@RequestBody CommandRequest commandRequest) { - commandManager.execute(commandRequest); - return ResponseEntity.ok(commandRequest); - } -} diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/publisher/CommandPublisher.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/publisher/CommandPublisher.java new file mode 100644 index 000000000..6fade9fba --- /dev/null +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/publisher/CommandPublisher.java @@ -0,0 +1,39 @@ +package com.solace.maas.ep.event.management.agent.publisher; + +import com.solace.maas.ep.event.management.agent.config.SolaceConfiguration; +import com.solace.maas.ep.event.management.agent.plugin.mop.MOPMessage; +import com.solace.maas.ep.event.management.agent.plugin.publisher.SolacePublisher; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +@ConditionalOnProperty(name = "event-portal.gateway.messaging.standalone", havingValue = "false") +public class CommandPublisher { + + private final SolacePublisher solacePublisher; + private final SolaceConfiguration solaceConfiguration; + + public CommandPublisher(SolacePublisher solacePublisher, SolaceConfiguration solaceConfiguration) { + this.solacePublisher = solacePublisher; + this.solaceConfiguration = solaceConfiguration; + } + + /** + * Sends the command response to EP. + *

+ * The topic for command response: + * sc/ep/runtime/{orgId}/{runtimeAgentId}/commandResponse/v1/{correlationId} + */ + + public void sendCommandResponse(MOPMessage message, Map topicDetails) { + + String topicString = + String.format("%scommandResponse/v1/%s", + solaceConfiguration.getTopicPrefix(), + topicDetails.get("correlationId")); + + solacePublisher.publish(message, topicString); + } +} diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/CommandMessageHandler.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/CommandMessageHandler.java new file mode 100644 index 000000000..e5b0962ef --- /dev/null +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/CommandMessageHandler.java @@ -0,0 +1,29 @@ +package com.solace.maas.ep.event.management.agent.subscriber; + +import com.solace.maas.ep.common.messages.CommandMessage; +import com.solace.maas.ep.event.management.agent.command.CommandManager; +import com.solace.maas.ep.event.management.agent.config.SolaceConfiguration; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@ConditionalOnProperty(name = "event-portal.gateway.messaging.standalone", havingValue = "false") +public class CommandMessageHandler extends SolaceMessageHandler { + + private final CommandManager commandManager; + + public CommandMessageHandler( + SolaceConfiguration solaceConfiguration, + SolaceSubscriber solaceSubscriber, CommandManager commandManager) { + super(solaceConfiguration.getTopicPrefix() + "command/v1/>", solaceSubscriber); + this.commandManager = commandManager; + } + + @Override + public void receiveMessage(String destinationName, CommandMessage message) { + log.debug("receiveMessage {}\n{}", destinationName, message); + commandManager.execute(message); + } +} diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/SolaceMessageHandler.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/SolaceMessageHandler.java index 6d323c253..24713d85f 100644 --- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/SolaceMessageHandler.java +++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/subscriber/SolaceMessageHandler.java @@ -20,6 +20,7 @@ import org.jboss.logging.MDC; import java.util.HashMap; +import java.util.List; import java.util.Map; @Slf4j @@ -69,19 +70,7 @@ public void onMessage(InboundMessage inboundMessage) { String receivedClassName = messageClass.getSimpleName(); - if ("ScanCommandMessage".equals(receivedClassName) || "ScanDataImportMessage".equals(receivedClassName)) { - Map map = objectMapper.readValue(messageAsString, Map.class); - String scanId = (String) map.get("scanId"); - String traceId = (String) map.get("traceId"); - String actorId = (String) map.get("actorId"); - String messagingServiceId = (String) map.get("messagingServiceId"); - - MDC.clear(); - MDC.put(RouteConstants.SCAN_ID, scanId); - MDC.put(RouteConstants.TRACE_ID, traceId); - MDC.put(RouteConstants.ACTOR_ID, actorId); - MDC.put(RouteConstants.MESSAGING_SERVICE_ID, messagingServiceId); - } + setupMDC(messageAsString, receivedClassName); message = (T) objectMapper.readValue(messageAsString, messageClass); log.trace("onMessage: {}\n{}", messageClass, messageAsString); @@ -92,5 +81,26 @@ public void onMessage(InboundMessage inboundMessage) { } } + private static void setupMDC(String messageAsString, String receivedClassName) throws JsonProcessingException { + List scanClassNames = List.of("ScanCommandMessage", "ScanDataImportMessage"); + List commandClassNames = List.of("CommandMessage"); + + Map map = objectMapper.readValue(messageAsString, Map.class); + + MDC.clear(); + MDC.put(RouteConstants.TRACE_ID, map.get("traceId")); + MDC.put(RouteConstants.ACTOR_ID, map.get("actorId")); + + if (scanClassNames.contains(receivedClassName)) { + MDC.put(RouteConstants.SCAN_ID, map.get("scanId")); + MDC.put(RouteConstants.MESSAGING_SERVICE_ID, map.get("messagingServiceId")); + } + + if (commandClassNames.contains(receivedClassName)) { + MDC.put(RouteConstants.COMMAND_CORRELATION_ID, map.get("correlationId")); + MDC.put(RouteConstants.MESSAGING_SERVICE_ID, map.get("serviceId")); + } + } + public abstract void receiveMessage(String destinationName, T message); } diff --git a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/TestConfig.java b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/TestConfig.java index e00a262e9..fc54bdf57 100644 --- a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/TestConfig.java +++ b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/TestConfig.java @@ -9,7 +9,10 @@ import com.solace.maas.ep.event.management.agent.plugin.manager.client.kafkaClient.KafkaClientReconnection; import com.solace.maas.ep.event.management.agent.plugin.manager.client.kafkaClient.KafkaClientReconnectionConfig; import com.solace.maas.ep.event.management.agent.plugin.messagingService.RtoMessageBuilder; +import com.solace.maas.ep.event.management.agent.plugin.service.MessagingServiceDelegateService; +import com.solace.maas.ep.event.management.agent.plugin.terraform.manager.TerraformManager; import com.solace.maas.ep.event.management.agent.plugin.vmr.VmrProcessor; +import com.solace.maas.ep.event.management.agent.publisher.CommandPublisher; import com.solace.maas.ep.event.management.agent.testConfigs.MessagingServiceTestConfig; import com.solace.maas.ep.event.management.agent.testConfigs.PublisherTestConfig; import com.solace.maas.ep.event.management.agent.util.IDGenerator; @@ -79,6 +82,18 @@ public VmrProcessor getVmrProcessor() { return processor; } + @Bean + @Primary + public MessagingServiceDelegateService getMessagingServiceDelegateService() { + return mock(MessagingServiceDelegateService.class); + } + + @Bean + @Primary + public TerraformManager getTerraformManager() { + return mock(TerraformManager.class); + } + @Bean @Primary public OutboundMessageBuilder outboundMessageBuilder() { @@ -119,6 +134,12 @@ public IDGenerator idGenerator() { return idGenerator; } + @Bean + @Primary + public CommandPublisher getCommandPublisher() { + return mock(CommandPublisher.class); + } + @Bean @Primary KafkaClientConnection kafkaClientConnection() { diff --git a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java new file mode 100644 index 000000000..b03bd596b --- /dev/null +++ b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java @@ -0,0 +1,106 @@ +package com.solace.maas.ep.event.management.agent.commandManager; + +import com.solace.maas.ep.common.messages.CommandMessage; +import com.solace.maas.ep.event.management.agent.TestConfig; +import com.solace.maas.ep.event.management.agent.command.CommandManager; +import com.solace.maas.ep.event.management.agent.config.eventPortal.EventPortalProperties; +import com.solace.maas.ep.event.management.agent.plugin.command.model.Command; +import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandBundle; +import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandType; +import com.solace.maas.ep.event.management.agent.plugin.command.model.ExecutionType; +import com.solace.maas.ep.event.management.agent.plugin.mop.MOPSvcType; +import com.solace.maas.ep.event.management.agent.plugin.service.MessagingServiceDelegateService; +import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SempClient; +import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SolaceHttpSemp; +import com.solace.maas.ep.event.management.agent.plugin.terraform.manager.TerraformManager; +import com.solace.maas.ep.event.management.agent.publisher.CommandPublisher; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Map; + +import static com.solace.maas.ep.event.management.agent.plugin.mop.MOPMessageType.generic; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ActiveProfiles("TEST") +@EnableAutoConfiguration +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestConfig.class) +public class CommandManagerTests { + + @Autowired + CommandManager commandManager; + + @Autowired + TerraformManager terraformManager; + + @Autowired + CommandPublisher commandPublisher; + + @Autowired + MessagingServiceDelegateService messagingServiceDelegateService; + + @Autowired + EventPortalProperties eventPortalProperties; + + @Test + public void testCommandManager() { + // Create a command request message + CommandMessage message = new CommandMessage(); + message.setOrigType(MOPSvcType.maasEventMgmt); + message.withMessageType(generic); + message.setContext("abc"); + message.setActorId("myActorId"); + message.setOrgId(eventPortalProperties.getOrganizationId()); + message.setTraceId("myTraceId"); + message.setCorrelationId("myCorrelationId"); + message.setCommandBundles(List.of( + CommandBundle.builder() + .executionType(ExecutionType.serial) + .exitOnFailure(false) + .commands(List.of( + Command.builder() + .commandType(CommandType.terraform) + .body("asdfasdfadsf") + .command("apply") + .build())) + .build())); + + doNothing().when(terraformManager).execute(any(), any(), any()); + + ArgumentCaptor> topicArgCaptor = ArgumentCaptor.forClass(Map.class); + doNothing().when(commandPublisher).sendCommandResponse(any(), any()); + when(messagingServiceDelegateService.getMessagingServiceClient(any())).thenReturn( + new SolaceHttpSemp(SempClient.builder() + .username("myUsername") + .password("myPassword") + .connectionUrl("myConnectionUrl") + .build())); + commandManager.execute(message); + + // Verify terraform manager is called + ArgumentCaptor> envArgCaptor = ArgumentCaptor.forClass(Map.class); + verify(terraformManager, times(1)).execute(any(), any(), envArgCaptor.capture()); + + // Verify the env vars are set with the terraform manager is called + Map envVars = envArgCaptor.getValue(); + assert envVars.get("TF_VAR_password").equals("myPassword"); + assert envVars.get("TF_VAR_username").equals("myUsername"); + assert envVars.get("TF_VAR_url").equals("myConnectionUrl"); + + verify(commandPublisher, times(1)).sendCommandResponse(any(), topicArgCaptor.capture()); + + Map topicVars = topicArgCaptor.getValue(); + assert topicVars.get("orgId").equals(eventPortalProperties.getOrganizationId()); + assert topicVars.get("runtimeAgentId").equals(eventPortalProperties.getRuntimeAgentId()); + assert topicVars.get("correlationId").equals(message.getCorrelationId()); + } +} diff --git a/service/plugin/pom.xml b/service/plugin/pom.xml index 5e2c427dc..67edc9047 100644 --- a/service/plugin/pom.xml +++ b/service/plugin/pom.xml @@ -121,6 +121,11 @@ lombok ${lombok.version} + + org.mapstruct + mapstruct + ${org.mapstruct.version} + org.apache.camel camel-reactive-streams @@ -128,4 +133,31 @@ compile + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + + + + + diff --git a/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/command/model/CommandResult.java b/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/command/model/CommandResult.java index 0be475ba9..64e6374c2 100644 --- a/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/command/model/CommandResult.java +++ b/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/command/model/CommandResult.java @@ -16,5 +16,4 @@ public class CommandResult { private JobStatus status; private Map result; private List> logs; - private List> errors; } \ No newline at end of file diff --git a/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/mop/MOPProtocol.java b/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/mop/MOPProtocol.java index 3bd33d4d1..27bee1627 100644 --- a/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/mop/MOPProtocol.java +++ b/service/plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/mop/MOPProtocol.java @@ -4,7 +4,7 @@ public enum MOPProtocol { scanData(2850), scanDataControl(2851), EMAHeartbeat(2852), - commandProtocol(2853); + epConfigPush(2853); private final int id; diff --git a/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformLogProcessingService.java b/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformLogProcessingService.java index df0ee6786..b73141f2c 100644 --- a/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformLogProcessingService.java +++ b/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformLogProcessingService.java @@ -6,6 +6,7 @@ import com.solace.maas.ep.event.management.agent.plugin.command.model.JobStatus; import com.solace.maas.ep.event.management.agent.plugin.terraform.configuration.TerraformProperties; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -28,6 +29,8 @@ public class TerraformLogProcessingService { public static final String VALUE_TYPE_APPLY_COMPLETE = "apply_complete"; public static final String VALUE_TYPE_APPLY_ERRORED = "apply_errored"; public static final String KEY_MESSAGE = "@message"; + public static final String KEY_LEVEL = "@level"; + public static final String KEY_TIMESTAMP = "@timestamp"; public static final String KEY_DIAGNOSTIC_DETAIL = "diagnosticDetail"; public static final String KEY_DIAGNOSTIC = "diagnostic"; private final TerraformProperties terraformProperties; @@ -76,8 +79,7 @@ public CommandResult buildTfCommandResult(List jsonLogs) { .toList(); JobStatus status = CollectionUtils.isEmpty(errorLogs) ? JobStatus.success : JobStatus.error; return CommandResult.builder() - .logs(successLogs) - .errors(errorLogs) + .logs(ListUtils.union(successLogs, errorLogs)) .status(status) .build(); @@ -105,7 +107,10 @@ private Map simplifyApplyCompleteLog(Map expande return Map.of( "address", extractResourceAddressFromHook(expandedLogMessage.get("hook")), - "message", expandedLogMessage.get(KEY_MESSAGE) + "message", expandedLogMessage.get(KEY_MESSAGE), + "level", expandedLogMessage.get(KEY_LEVEL).toString().toUpperCase(), + "timestamp", expandedLogMessage.get(KEY_TIMESTAMP) + ); } @@ -119,7 +124,11 @@ private Map simplifyApplyErroredLog(Map expanded //return expandedLogMessage; return Map.of( "address", extractResourceAddressFromDiagnostic(expandedLogMessage.get(KEY_DIAGNOSTIC)), - KEY_DIAGNOSTIC_DETAIL, extractDiagnosticDetailMessage(expandedLogMessage.get(KEY_DIAGNOSTIC)) + KEY_DIAGNOSTIC_DETAIL, extractDiagnosticDetailMessage(expandedLogMessage.get(KEY_DIAGNOSTIC)), + "message", expandedLogMessage.get(KEY_MESSAGE), + "level", expandedLogMessage.get(KEY_LEVEL).toString().toUpperCase(), + "timestamp", expandedLogMessage.get(KEY_TIMESTAMP) + ); } diff --git a/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformManager.java b/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformManager.java index 6eeeef6be..2729f5643 100644 --- a/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformManager.java +++ b/service/terraform-plugin/src/main/java/com/solace/maas/ep/event/management/agent/plugin/terraform/manager/TerraformManager.java @@ -17,6 +17,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Base64; import java.util.List; @@ -29,6 +30,7 @@ @Service @Slf4j public class TerraformManager { + public static final String LOG_LEVEL_ERROR = "ERROR"; private final TerraformLogProcessingService terraformLogProcessingService; private final TerraformProperties terraformProperties; private final TerraformClientFactory terraformClientFactory; @@ -102,7 +104,6 @@ private void processTerraformResponse(CommandRequest request, Command command, S command.setResult(CommandResult.builder() .status(JobStatus.success) .logs(List.of()) - .errors(List.of()) .build()); } else { if (!"write_HCL".equals(commandVerb)) { @@ -112,7 +113,6 @@ private void processTerraformResponse(CommandRequest request, Command command, S command.setResult(CommandResult.builder() .status(JobStatus.success) .logs(List.of()) - .errors(List.of()) .build()); } } @@ -121,10 +121,11 @@ private void processTerraformResponse(CommandRequest request, Command command, S private void setCommandError(Command command, Exception e) { command.setResult(CommandResult.builder() .status(JobStatus.error) - .logs(List.of()) - .errors(List.of( + .logs(List.of( Map.of("message", e.getMessage(), - "errorType", e.getClass().getName()))) + "errorType", e.getClass().getName(), + "level", LOG_LEVEL_ERROR, + "timestamp", OffsetDateTime.now()))) .build()); } diff --git a/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/TerraformCommandIT.java b/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/TerraformCommandIT.java index 70641a490..bf26088dc 100644 --- a/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/TerraformCommandIT.java +++ b/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/TerraformCommandIT.java @@ -111,6 +111,7 @@ public void testCreateResourceHappyPath() throws IOException { assertEquals(JobStatus.success, result.getStatus()); assertTrue(result.getLogs().get(2).get("message").toString().contains("Creation complete after")); assertTrue(result.getLogs().get(3).get("message").toString().contains("Creation complete after")); + assertAllLogsContainExpectedFields(result.getLogs()); } } } @@ -142,7 +143,11 @@ public void testCreateResourceTerraformErrorFailurePath() throws IOException { CommandResult result = tfCommand.getResult(); assertEquals(JobStatus.error, result.getStatus()); - assertTrue(result.getErrors().get(0).get("diagnosticDetail").toString().contains("Subscription a/b/c/> already exists")); + List> errorLogs = result.getLogs().stream() + .filter(log -> log.get("level").equals("ERROR")) + .toList(); + assertTrue(errorLogs.get(0).get("diagnosticDetail").toString().contains("Subscription a/b/c/> already exists")); + assertAllLogsContainExpectedFields(result.getLogs()); } } } @@ -164,8 +169,9 @@ public void testCreateResourceMissingParameterFailurePath() { CommandResult result = tfCommand.getResult(); assertEquals(JobStatus.error, result.getStatus()); - assertTrue(result.getErrors().get(0).get("errorType").toString().contains("java.lang.IllegalArgumentException")); - assertTrue(result.getErrors().get(0).get("message").toString().contains("Missing Content-Encoding property in command parameters.")); + assertTrue(result.getLogs().get(0).get("errorType").toString().contains("java.lang.IllegalArgumentException")); + assertTrue(result.getLogs().get(0).get("message").toString().contains("Missing Content-Encoding property in command parameters.")); + assertAllLogsContainExpectedFields(result.getLogs()); } } } @@ -196,8 +202,9 @@ public void testCreateResourceNoLogsFailurePath() throws IOException { CommandResult result = tfCommand.getResult(); assertEquals(JobStatus.error, result.getStatus()); - assertTrue(result.getErrors().get(0).get("errorType").toString().contains("java.lang.IllegalArgumentException")); - assertTrue(result.getErrors().get(0).get("message").toString().contains("No terraform logs were collected. Unable to process response.")); + assertTrue(result.getLogs().get(0).get("errorType").toString().contains("java.lang.IllegalArgumentException")); + assertTrue(result.getLogs().get(0).get("message").toString().contains("No terraform logs were collected. Unable to process response.")); + assertAllLogsContainExpectedFields(result.getLogs()); } } } @@ -218,8 +225,9 @@ public void testCreateResourceUnknownCommandFailurePath() { CommandResult result = tfCommand.getResult(); assertEquals(JobStatus.error, result.getStatus()); - assertTrue(result.getErrors().get(0).get("errorType").toString().contains("java.lang.IllegalArgumentException")); - assertTrue(result.getErrors().get(0).get("message").toString().contains("Unsupported command appply")); + assertTrue(result.getLogs().get(0).get("errorType").toString().contains("java.lang.IllegalArgumentException")); + assertTrue(result.getLogs().get(0).get("message").toString().contains("Unsupported command appply")); + assertAllLogsContainExpectedFields(result.getLogs()); } } } @@ -252,7 +260,6 @@ public void testIgnoreResult() throws IOException { CommandResult result = tfCommand.getResult(); assertEquals(JobStatus.success, result.getStatus()); assertTrue(result.getLogs().isEmpty()); - assertTrue(result.getErrors().isEmpty()); } } } @@ -286,6 +293,7 @@ public void testDeleteResourceHappyPath() throws IOException { assertEquals(JobStatus.success, result.getStatus()); assertTrue(result.getLogs().get(2).get("message").toString().contains("Destruction complete after")); assertTrue(result.getLogs().get(3).get("message").toString().contains("Destruction complete after")); + assertAllLogsContainExpectedFields(result.getLogs()); } } } @@ -354,4 +362,12 @@ private static List getResourceAsStringArray(Resource resource) { throw new UncheckedIOException(e); } } + + private void assertAllLogsContainExpectedFields(List> logs) { + for (Map log : logs) { + assertTrue(log.containsKey("message")); + assertTrue(log.containsKey("level")); + assertTrue(log.containsKey("timestamp")); + } + } } diff --git a/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/real/TerraformClientRealTests.java b/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/real/TerraformClientRealTests.java index 39f9deddf..04ca884f0 100644 --- a/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/real/TerraformClientRealTests.java +++ b/service/terraform-plugin/src/test/java/com/solace/maas/ep/event/management/agent/plugin/terraform/real/TerraformClientRealTests.java @@ -130,7 +130,6 @@ public void importResource() { for (Command command : commandBundle.getCommands()) { CommandResult result = command.getResult(); System.out.println("Logs " + result.getLogs()); - System.out.println("Errors " + result.getErrors()); assertNotSame(JobStatus.error, result.getStatus()); } } @@ -169,7 +168,6 @@ private List executeTerraformCommand(String hclFileName, String t for (Command command : commandBundle.getCommands()) { CommandResult result = command.getResult(); System.out.println("Logs " + result.getLogs()); - System.out.println("Errors " + result.getErrors()); assertNotSame(JobStatus.error, result.getStatus()); } }