From e3c67dff35e6acddca50866e9a7fa2e0e2c58211 Mon Sep 17 00:00:00 2001 From: jinyangyang222 Date: Mon, 21 Aug 2023 18:35:01 +0800 Subject: [PATCH] update terraform-boot healthcheck method --- .../boot/api/TerraformApiController.java | 12 ++--- .../TerraformApiExceptionHandler.java | 12 +++++ .../TerraformHealthCheckException.java | 16 +++++++ .../boot/models/response/ResultType.java | 3 +- .../boot/terraform/TerraformExecutor.java | 48 +++++++++++++++++++ 5 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformHealthCheckException.java diff --git a/src/main/java/org/eclipse/xpanse/terraform/boot/api/TerraformApiController.java b/src/main/java/org/eclipse/xpanse/terraform/boot/api/TerraformApiController.java index 9bb4e6b..732d62f 100644 --- a/src/main/java/org/eclipse/xpanse/terraform/boot/api/TerraformApiController.java +++ b/src/main/java/org/eclipse/xpanse/terraform/boot/api/TerraformApiController.java @@ -11,7 +11,6 @@ import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.eclipse.xpanse.terraform.boot.models.SystemStatus; -import org.eclipse.xpanse.terraform.boot.models.enums.HealthStatus; import org.eclipse.xpanse.terraform.boot.models.request.TerraformDeployRequest; import org.eclipse.xpanse.terraform.boot.models.request.TerraformDestroyRequest; import org.eclipse.xpanse.terraform.boot.models.request.async.TerraformAsyncDeployRequest; @@ -55,12 +54,13 @@ public TerraformApiController(TerraformExecutor terraformExecutor) { */ @Tag(name = "Terraform", description = "APIs for running Terraform commands") @Operation(description = "Check health of Terraform API service") - @GetMapping(value = "/health", produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "/health/{module_directory}", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.OK) - public SystemStatus healthCheck() { - SystemStatus systemStatus = new SystemStatus(); - systemStatus.setHealthStatus(HealthStatus.OK); - return systemStatus; + public SystemStatus healthCheck( + @Parameter(name = "module_directory", + description = "directory name where the Terraform module files exist.") + @PathVariable("module_directory") String moduleDirectory) { + return this.terraformExecutor.tfHealthCheck(moduleDirectory); } /** diff --git a/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformApiExceptionHandler.java b/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformApiExceptionHandler.java index 9406d5e..32103c2 100644 --- a/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformApiExceptionHandler.java +++ b/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformApiExceptionHandler.java @@ -92,4 +92,16 @@ public Response handleHttpMessageConversionException(HttpMessageConversionExcept return Response.errorResponse(ResultType.BAD_PARAMETERS, Collections.singletonList(failMessage)); } + + /** + * Exception handler for TerraformHealthCheckException. + */ + @ExceptionHandler({TerraformHealthCheckException.class}) + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) + public Response handleTerraformHealthCheckException(TerraformHealthCheckException ex) { + log.error("TerraformHealthCheckException: ", ex); + String failMessage = ex.getMessage(); + return Response.errorResponse(ResultType.SERVICE_UNAVAILABLE, + Collections.singletonList(failMessage)); + } } diff --git a/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformHealthCheckException.java b/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformHealthCheckException.java new file mode 100644 index 0000000..e75978b --- /dev/null +++ b/src/main/java/org/eclipse/xpanse/terraform/boot/models/exceptions/TerraformHealthCheckException.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.terraform.boot.models.exceptions; + +/** + * Used to indicate Terraform health check anomalies. + */ +public class TerraformHealthCheckException extends RuntimeException { + + public TerraformHealthCheckException(String message) { + super(message); + } +} diff --git a/src/main/java/org/eclipse/xpanse/terraform/boot/models/response/ResultType.java b/src/main/java/org/eclipse/xpanse/terraform/boot/models/response/ResultType.java index 17658d2..480488a 100644 --- a/src/main/java/org/eclipse/xpanse/terraform/boot/models/response/ResultType.java +++ b/src/main/java/org/eclipse/xpanse/terraform/boot/models/response/ResultType.java @@ -44,7 +44,8 @@ public enum ResultType { ACCESS_DENIED("Access Denied"), SENSITIVE_FIELD_ENCRYPTION_DECRYPTION_EXCEPTION("Sensitive " + "Field Encryption Or Decryption Failed Exception"), - UNSUPPORTED_ENUM_VALUE("Unsupported Enum Value"); + UNSUPPORTED_ENUM_VALUE("Unsupported Enum Value"), + SERVICE_UNAVAILABLE("Service Unavailable"); private final String value; diff --git a/src/main/java/org/eclipse/xpanse/terraform/boot/terraform/TerraformExecutor.java b/src/main/java/org/eclipse/xpanse/terraform/boot/terraform/TerraformExecutor.java index 7556865..f478c26 100644 --- a/src/main/java/org/eclipse/xpanse/terraform/boot/terraform/TerraformExecutor.java +++ b/src/main/java/org/eclipse/xpanse/terraform/boot/terraform/TerraformExecutor.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.util.Collections; @@ -18,7 +19,10 @@ import java.util.Objects; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.eclipse.xpanse.terraform.boot.models.SystemStatus; +import org.eclipse.xpanse.terraform.boot.models.enums.HealthStatus; import org.eclipse.xpanse.terraform.boot.models.exceptions.TerraformExecutorException; +import org.eclipse.xpanse.terraform.boot.models.exceptions.TerraformHealthCheckException; import org.eclipse.xpanse.terraform.boot.models.request.TerraformDeployRequest; import org.eclipse.xpanse.terraform.boot.models.request.TerraformDestroyRequest; import org.eclipse.xpanse.terraform.boot.models.request.async.TerraformAsyncDeployRequest; @@ -41,6 +45,13 @@ public class TerraformExecutor { private static final String VARS_FILE_NAME = "variables.tfvars.json"; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final String OUTPUT_CONFIG_FILE_NAME = "hello-world.tf"; + private static final String HELLO_WORLD_TEMPLATE = """ + output "hello_world" { + value = "Hello, World!" + } + """; + private static final String HEALTH_CHECK_DIR = "health_check"; static { OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); @@ -254,6 +265,43 @@ public TerraformValidationResult tfValidate(String moduleDirectory) { } } + /** + * Performs a Terraform health check for the specified module directory. + * + * @return SystemStatus. + */ + public SystemStatus tfHealthCheck(String moduleDirectory) { + String filePath = + getModuleFullPath(moduleDirectory) + File.separator + HEALTH_CHECK_DIR + + File.separator + OUTPUT_CONFIG_FILE_NAME; + try { + File file = new File(filePath); + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + FileWriter writer = new FileWriter(filePath); + writer.write(HELLO_WORLD_TEMPLATE); + writer.close(); + } catch (IOException e) { + throw new TerraformHealthCheckException( + "Error creating or writing to file '" + filePath + "': " + e.getMessage()); + } + tfValidate(moduleDirectory + File.separator + HEALTH_CHECK_DIR); + SystemCmdResult systemCmdResult = + execute("terraform destroy -auto-approve -input=false -no-color ", + getModuleFullPath(moduleDirectory + + File.separator + HEALTH_CHECK_DIR), + new HashMap<>()); + SystemStatus systemStatus = new SystemStatus(); + if (systemCmdResult.isCommandSuccessful()) { + systemStatus.setHealthStatus(HealthStatus.OK); + return systemStatus; + } + systemStatus.setHealthStatus(HealthStatus.NOK); + return systemStatus; + } + private String getModuleFullPath(String moduleDirectory) { return this.moduleParentDirectoryPath + File.separator + moduleDirectory; }