Skip to content

Commit

Permalink
log download on failure
Browse files Browse the repository at this point in the history
  • Loading branch information
laDok8 committed Mar 5, 2024
1 parent 5b9990b commit d59a8a9
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Azure-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,7 @@ You can deploy archive to running EAP instance. See [here](azure-wildfly/src/tes
###### Web application
You can deploy archive to running EAP instance. See [here](azure-wildfly/src/test/java/sunstone/azure/armTemplates/archiveDeploy/webapp/suitetests/AzureWebAppDeployFirstTest.java)

### log downloader [WIP: azure only]

activityLog Downloader starting Time is configurable by `sunstone.azure.logDownloadTimeStart`, which is offset from current time. Default is 1 hour.
API is restricted by RGName but not specific RG, potentially fetching logs from previous RGs with the same name.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ you don't have to do anything. For other loggers, see [the SLF4J manual](http://

The loggers are called `sunstone.*`, short and clear. (For example: `sunstone.core`)

### log downloader [WIP: azure only]

In the event of a deployment failure, logs are automatically downloaded and stored within the `MODULE/target/logs/*.log` files.
Please note that this feature is currently a work in progress and is available for Azure environments only.

## License

* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,29 @@


import com.azure.core.management.Region;
import com.azure.core.util.polling.LongRunningOperationStatus;
import com.azure.core.util.polling.PollResponse;
import com.azure.resourcemanager.AzureResourceManager;
import com.azure.resourcemanager.resources.fluentcore.model.Accepted;
import com.azure.resourcemanager.resources.models.Deployment;
import com.azure.resourcemanager.resources.models.DeploymentMode;
import com.azure.resourcemanager.resources.models.ResourceGroups;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.slf4j.Logger;
import sunstone.core.TimeoutUtils;

import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import static sunstone.core.SunstoneConfig.getValue;

/**
* Purpose: the class handles Azure template - deploy and undeploy the template to and from a stack.
Expand Down Expand Up @@ -60,12 +67,35 @@ void deploy(String template, Map<String, String> parameters, String group, Strin
LOGGER.warn("Azure resource group '{}' already exists! It will be reused and deleted when tests are finished.", group);
}

armManager.deployments().define(deploymentName)
//.create() doesn't allow status check and fails
Accepted<Deployment> acceptedDeployment = armManager.deployments().define(deploymentName)
.withExistingResourceGroup(group)
.withTemplate(template)
.withParameters(parametersFromMap(template, parameters))
.withMode(DeploymentMode.INCREMENTAL)
.create();
.beginCreate();

// polling wait
final Duration pollInterval = Duration.ofSeconds(TimeoutUtils.adjust(getValue(AzureConfig.DEPLOY_POLL_WAIT, 1)));
LongRunningOperationStatus pollStatus = acceptedDeployment.getActivationResponse().getStatus();
long delayInMills = pollInterval.toMillis();
while (!pollStatus.isComplete()) {
try {
Thread.sleep(delayInMills);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

PollResponse<?> pollResponse = acceptedDeployment.getSyncPoller().poll();
pollStatus = pollResponse.getStatus();
}
if (pollStatus != LongRunningOperationStatus.SUCCESSFULLY_COMPLETED) {
LOGGER.error("Azure deployment from template {} in \"{}\" group failed", deploymentName, group);
AzureUtils.downloadResourceGroupLogs(armManager, group);
undeploy(group);
throw new RuntimeException("Deployment failed for group:" + group);
}

LOGGER.debug("Azure deployment from template {} in \"{}\" group is ready", deploymentName, group);
}

Expand Down
2 changes: 2 additions & 0 deletions azure/src/main/java/sunstone/azure/impl/AzureConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ public class AzureConfig {
public static final String PASSWORD = "sunstone.azure.password";
public static final String REGION = "sunstone.azure.region";
public static final String GROUP = "sunstone.azure.group";
public static final String DEPLOY_POLL_WAIT = "sunstone.azure.deployPollWait";
public static final String RG_LOGGER_TIME_START = "sunstone.azure.logDownloadTimeStart";
}
65 changes: 65 additions & 0 deletions azure/src/main/java/sunstone/azure/impl/AzureUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,34 @@


import com.azure.core.credential.TokenCredential;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.management.AzureEnvironment;
import com.azure.core.management.exception.ManagementError;
import com.azure.core.management.profile.AzureProfile;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.azure.resourcemanager.AzureResourceManager;
import com.azure.resourcemanager.appservice.models.AppServicePlan;
import com.azure.resourcemanager.appservice.models.WebApp;
import com.azure.resourcemanager.compute.models.VirtualMachine;
import com.azure.resourcemanager.monitor.models.EventData;
import com.azure.resourcemanager.postgresqlflexibleserver.PostgreSqlManager;
import com.azure.resourcemanager.postgresqlflexibleserver.models.Server;
import org.slf4j.Logger;
import sunstone.core.SunstoneConfig;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static sunstone.core.SunstoneConfig.getValue;

public class AzureUtils {
static Logger LOGGER = AzureLogger.DEFAULT;
static AzureResourceManager getResourceManager() {
return AzureResourceManager
.authenticate(getCredentials(), new AzureProfile(AzureEnvironment.AZURE))
Expand Down Expand Up @@ -73,4 +87,55 @@ static Optional<Server> findAzurePgSqlServer(PostgreSqlManager pgsqlManager, Str
return Optional.empty();
}
}

/**
* API is restricted by RGName but not specific RG, potentially fetching logs from previous RGs with the same name.
*/
static void downloadResourceGroupLogs(AzureResourceManager armManager, String rgName) {
PagedIterable<EventData> x = armManager.activityLogs().defineQuery()
.startingFrom(OffsetDateTime.now().minusHours(getValue(AzureConfig.RG_LOGGER_TIME_START, 1)))
.endsBefore(OffsetDateTime.now())
.withAllPropertiesInResponse()
.filterByResourceGroup(rgName)
.execute();

Path logDir = Paths.get("logs");
if (!Files.exists(logDir)) {
try {
Files.createDirectories(logDir);
} catch (IOException e) {
LOGGER.error("Error creating log directory", e);
return;
}
}

Path activityLogFile = logDir.resolve(rgName + "-activity-log.log");
try(java.io.FileWriter writer = new java.io.FileWriter(activityLogFile.toString())) {
for (EventData e : x) {
writer.write(e.operationName().localizedValue() + "\tname: " + e.eventName().localizedValue() + "\tprops: " + e.properties() + "\tdesc: " + e.description() + "\ttimestamp: " + e.eventTimestamp() + "\tlevel: " + e.level() + "\n\n");
}
} catch (IOException e) {
LOGGER.error("Error writing activity log", e);
}

Path deploymentLogFile = logDir.resolve(rgName + "-deployments.log");
try(java.io.FileWriter writer = new java.io.FileWriter(deploymentLogFile.toString())) {
armManager.deployments().listByResourceGroup(rgName).forEach(d -> {
List<String> operations = d.deploymentOperations().list().stream().map(s -> "\n" + s.timestamp() + ", state: " + s.provisioningState() + ", op: " + s.provisioningOperation() + ", resource: " + (s.targetResource() != null ? s.targetResource().resourceName() : "UNKNOWN_NAME") + ", type: " + (s.targetResource() != null ? s.targetResource().resourceType() : "UNKNOWN_TYPE")).collect(Collectors.toList());
try {
writer.write(d.error().getMessage());
writer.write("\nError message: \n");
writer.write(d.error().getDetails().stream().map(ManagementError::getMessage).collect(Collectors.joining()));
writer.write("\nDeployment Info:\n");
writer.write(d.name() + "\t" + d.provisioningState() + "\t" + d.timestamp() + "\twith:" + operations + "\n");
writer.write("\n\n");
} catch (IOException e) {
LOGGER.error("Error writing deployment log", e);
}
});
} catch (IOException e) {
LOGGER.error("Error writing deployment log", e);
}
}

}

0 comments on commit d59a8a9

Please sign in to comment.