From 8dfa11a50b62f989f0ed01e214c019794bcd7413 Mon Sep 17 00:00:00 2001 From: baixinsui Date: Fri, 11 Oct 2024 16:56:51 +0800 Subject: [PATCH] extend terraform and opentofu local deployers to support multiple versions --- .gitignore | 3 +- .../handler/DeploymentExceptionHandler.java | 12 + .../modules/cache/CaffeineCacheConfig.java | 6 + .../modules/cache/RedisCacheConfig.java | 36 +- .../modules/cache/consts/CacheConstants.java | 4 + modules/deployment/pom.xml | 10 + .../modules/deployment/DeployService.java | 3 +- .../deployment/ServiceOrderManager.java | 3 +- .../deployment/ServiceStateManager.java | 5 +- .../deployertools/DeployerToolUtils.java | 430 ++++++++++++++++++ .../DeployerToolVersionsCache.java | 57 +++ .../DeployerToolVersionsCacheManager.java | 120 +++++ .../DeployerToolVersionsFetcher.java | 137 ++++++ .../opentofulocal/OpenTofuInstaller.java | 100 ++++ .../OpenTofuLocalDeployment.java | 52 +-- .../opentofulocal/OpenTofuLocalExecutor.java | 25 +- .../terraformlocal/TerraformInstaller.java | 97 ++++ .../TerraformLocalDeployment.java | 52 +-- .../TerraformLocalExecutor.java | 27 +- .../OpenTofuLocalDeploymentTest.java | 51 ++- .../OpenTofuLocalExecutorTest.java | 6 +- .../TerraformLocalDeploymentTest.java | 46 +- .../TerraformLocalExecutorTest.java | 2 +- .../InvalidDeployerToolException.java | 15 + .../modules/models/response/ResultType.java | 3 +- .../models/servicetemplate/DeployerTool.java | 11 +- .../validators/DeployerVersionConstraint.java | 39 -- .../validators/DeployerVersionValidator.java | 41 -- pom.xml | 6 + .../resources/application-test.properties | 11 +- .../src/main/resources/application.properties | 12 +- .../xpanse/runtime/AdminServicesApiTest.java | 3 +- .../xpanse/runtime/ArchitectureTests.java | 1 + .../xpanse/runtime/AuthorizationApiTest.java | 2 +- .../runtime/CredentialsConfigApiTest.java | 2 +- .../runtime/CspServiceTemplateApiTest.java | 2 +- .../ExistingCloudResourcesApiTest.java | 8 +- .../runtime/IsvCloudCredentialsApiTest.java | 2 +- .../runtime/IsvServiceDeployerApiTest.java | 2 +- .../runtime/OpenTofuMakerWebhookApiTest.java | 4 +- .../xpanse/runtime/ServiceCatalogApiTest.java | 3 +- .../runtime/ServiceConfigurationApiTest.java | 5 +- .../runtime/ServiceDeployerApiTest.java | 7 +- .../xpanse/runtime/ServiceMetricsApiTest.java | 2 +- .../runtime/ServiceMigrationApiTest.java | 3 +- .../runtime/ServiceOrderManageApiTest.java | 3 +- .../runtime/ServicePolicyManageApiTest.java | 3 +- .../xpanse/runtime/ServicePricingApiTest.java | 2 +- .../runtime/ServiceStateManageApiTest.java | 7 +- .../runtime/ServiceTemplateApiTest.java | 2 +- .../runtime/TerraformBootWebhookApiTest.java | 3 +- .../runtime/UserCloudCredentialsApiTest.java | 2 +- .../runtime/UserPolicyManageApiTest.java | 2 +- .../xpanse/runtime/WorkFlowApiTest.java | 2 +- .../AdminHealthCheckWithRedisCacheTest.java | 2 +- .../redis/UserCredentialsWithRedisTest.java | 2 +- .../AdminHealthCheckWithMysqlDbTest.java | 3 +- .../mysql/DeploymentWithMysqlTest.java | 2 +- .../mysql/RegistrationWithMysqlTest.java | 2 +- .../common/openapi/OpenApiCommonTest.java | 2 +- .../CredentialOpenApiGeneratorTest.java | 2 +- .../deployment/OpenTofuInstallerTest.java | 59 +++ .../deployment/TerraformInstallerTest.java | 63 +++ .../orcherstrator/PluginManagerTest.java | 3 +- ...RequiredRoleDescriptionCustomizerTest.java | 2 +- .../openapi/OpenApiFileValidationTest.java | 3 +- 66 files changed, 1340 insertions(+), 299 deletions(-) create mode 100644 modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolUtils.java create mode 100644 modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCache.java create mode 100644 modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCacheManager.java create mode 100644 modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsFetcher.java create mode 100644 modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuInstaller.java create mode 100644 modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformInstaller.java create mode 100644 modules/models/src/main/java/org/eclipse/xpanse/modules/models/common/exceptions/InvalidDeployerToolException.java delete mode 100644 modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionConstraint.java delete mode 100644 modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionValidator.java create mode 100644 runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/OpenTofuInstallerTest.java create mode 100644 runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/TerraformInstallerTest.java diff --git a/.gitignore b/.gitignore index 4b6b997ed..7ff6e07e9 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ node_modules xpanse_deploy_ws /openapi .mvn/wrapper/*.jar -aes_sec \ No newline at end of file +aes_sec +terraform.log \ No newline at end of file diff --git a/modules/api/src/main/java/org/eclipse/xpanse/api/exceptions/handler/DeploymentExceptionHandler.java b/modules/api/src/main/java/org/eclipse/xpanse/api/exceptions/handler/DeploymentExceptionHandler.java index ff90e8ab8..4a24d50cb 100644 --- a/modules/api/src/main/java/org/eclipse/xpanse/api/exceptions/handler/DeploymentExceptionHandler.java +++ b/modules/api/src/main/java/org/eclipse/xpanse/api/exceptions/handler/DeploymentExceptionHandler.java @@ -12,6 +12,7 @@ import org.eclipse.xpanse.modules.deployment.deployers.terraform.exceptions.TerraformBootRequestFailedException; import org.eclipse.xpanse.modules.deployment.deployers.terraform.exceptions.TerraformExecutorException; import org.eclipse.xpanse.modules.models.billing.exceptions.ServicePriceCalculationFailed; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; import org.eclipse.xpanse.modules.models.response.Response; import org.eclipse.xpanse.modules.models.response.ResultType; import org.eclipse.xpanse.modules.models.service.deploy.exceptions.ActivitiTaskNotFoundException; @@ -258,4 +259,15 @@ public Response handleServicePriceCalculationFailed(ServicePriceCalculationFaile public Response handleFileLockedException(FileLockedException ex) { return getErrorResponse(ResultType.FILE_LOCKED, Collections.singletonList(ex.getMessage())); } + + /** + * Exception handler for InvalidDeployerToolException. + */ + @ExceptionHandler({InvalidDeployerToolException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public Response handleInvalidDeployerToolException(InvalidDeployerToolException ex) { + return getErrorResponse(ResultType.INVALID_DEPLOYER_TOOL, + Collections.singletonList(ex.getMessage())); + } } diff --git a/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/CaffeineCacheConfig.java b/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/CaffeineCacheConfig.java index d992f4cd3..8c04217f3 100644 --- a/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/CaffeineCacheConfig.java +++ b/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/CaffeineCacheConfig.java @@ -8,6 +8,7 @@ import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.CREDENTIAL_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.DEFAULT_CACHE_EXPIRE_TIME_IN_MINUTES; +import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.DEPLOYER_VERSIONS_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.MONITOR_METRICS_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.REGION_AZS_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.SERVICE_FLAVOR_PRICE_CACHE_NAME; @@ -56,6 +57,7 @@ public CacheManager caffeineCacheManager() { getServiceFlavorPriceCache()); cacheManager.registerCustomCache(CREDENTIAL_CACHE_NAME, getCredentialsCache()); cacheManager.registerCustomCache(MONITOR_METRICS_CACHE_NAME, getMonitorMetricsCache()); + cacheManager.registerCustomCache(DEPLOYER_VERSIONS_CACHE_NAME, getDeployerVersionsCache()); return cacheManager; } @@ -71,6 +73,10 @@ private Cache getServiceFlavorPriceCache() { return Caffeine.newBuilder().expireAfterWrite(duration, TimeUnit.MINUTES).build(); } + private Cache getDeployerVersionsCache() { + return Caffeine.newBuilder().build(); + } + private Cache getCredentialsCache() { return Caffeine.newBuilder() diff --git a/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/RedisCacheConfig.java b/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/RedisCacheConfig.java index 194c9e8d7..080ae4f0d 100644 --- a/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/RedisCacheConfig.java +++ b/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/RedisCacheConfig.java @@ -10,6 +10,7 @@ import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.CREDENTIAL_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.DEFAULT_CACHE_EXPIRE_TIME_IN_MINUTES; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.DEFAULT_CREDENTIAL_CACHE_EXPIRE_TIME_IN_SECONDS; +import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.DEPLOYER_VERSIONS_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.MONITOR_METRICS_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.REGION_AZS_CACHE_NAME; import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.SERVICE_FLAVOR_PRICE_CACHE_NAME; @@ -83,6 +84,7 @@ public CacheManager redisCacheManager() { getServiceFlavorPriceCache()); builder.withCacheConfiguration(CREDENTIAL_CACHE_NAME, getCredentialCache()); builder.withCacheConfiguration(MONITOR_METRICS_CACHE_NAME, getMonitorMetricsCache()); + builder.withCacheConfiguration(DEPLOYER_VERSIONS_CACHE_NAME, getDeployerVersionsCache()); return builder.build(); } @@ -123,9 +125,10 @@ private String getRedisEndpoint() { private RedisCacheConfiguration getRegionAzsCache() { long duration = regionAzsCacheDuration > 0 ? regionAzsCacheDuration : DEFAULT_CACHE_EXPIRE_TIME_IN_MINUTES; - return RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(duration)) - .serializeKeysWith(getRedisKeySerializer()) - .serializeValuesWith(getRedisValueSerializer()); + return RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(duration)) + .serializeKeysWith(getStringRedisSerializer()) + .serializeValuesWith(getJsonRedisSerializer()); } private RedisCacheConfiguration getServiceFlavorPriceCache() { @@ -133,14 +136,14 @@ private RedisCacheConfiguration getServiceFlavorPriceCache() { : DEFAULT_CACHE_EXPIRE_TIME_IN_MINUTES; return RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(duration)) - .serializeKeysWith(getRedisKeySerializer()) - .serializeValuesWith(getRedisValueSerializer()); + .serializeKeysWith(getStringRedisSerializer()) + .serializeValuesWith(getJsonRedisSerializer()); } private RedisCacheConfiguration getCredentialCache() { return RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(DEFAULT_CREDENTIAL_CACHE_EXPIRE_TIME_IN_SECONDS)) - .serializeKeysWith(getRedisKeySerializer()) + .serializeKeysWith(getStringRedisSerializer()) .serializeValuesWith(getCredentialValueSerializer()); } @@ -149,8 +152,14 @@ private RedisCacheConfiguration getMonitorMetricsCache() { : DEFAULT_CACHE_EXPIRE_TIME_IN_MINUTES; return RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(duration)) - .serializeKeysWith(getRedisKeySerializer()) - .serializeValuesWith(getRedisValueSerializer()); + .serializeKeysWith(getStringRedisSerializer()) + .serializeValuesWith(getJsonRedisSerializer()); + } + + private RedisCacheConfiguration getDeployerVersionsCache() { + return RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(getStringRedisSerializer()) + .serializeValuesWith(getJdkRedisSerializer()); } @@ -200,16 +209,23 @@ private void checkRedisIsAvailable() throws RuntimeException { } - private RedisSerializationContext.SerializationPair getRedisKeySerializer() { + private RedisSerializationContext.SerializationPair getStringRedisSerializer() { return RedisSerializationContext.SerializationPair.fromSerializer( new StringRedisSerializer()); } - private RedisSerializationContext.SerializationPair getRedisValueSerializer() { + // Jackson2JsonRedisSerializer for Object. + private RedisSerializationContext.SerializationPair getJsonRedisSerializer() { return RedisSerializationContext.SerializationPair.fromSerializer( new Jackson2JsonRedisSerializer<>(Object.class)); } + // JdkSerializationRedisSerializer for Set, Array, Map etc. + private RedisSerializationContext.SerializationPair getJdkRedisSerializer() { + return RedisSerializationContext.SerializationPair.fromSerializer( + new JdkSerializationRedisSerializer()); + } + private RedisSerializationContext .SerializationPair getCredentialValueSerializer() { return RedisSerializationContext.SerializationPair.fromSerializer( diff --git a/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/consts/CacheConstants.java b/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/consts/CacheConstants.java index 2222410e2..7959be3ce 100644 --- a/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/consts/CacheConstants.java +++ b/modules/cache/src/main/java/org/eclipse/xpanse/modules/cache/consts/CacheConstants.java @@ -19,8 +19,12 @@ public class CacheConstants { public static final String MONITOR_METRICS_CACHE_NAME = "MONITOR_METRICS_CACHE"; + public static final String DEPLOYER_VERSIONS_CACHE_NAME = "DEPLOYER_VERSIONS_CACHE"; + public static final int DEFAULT_CACHE_EXPIRE_TIME_IN_MINUTES = 60; + public static final int DEFAULT_CREDENTIAL_CACHE_EXPIRE_TIME_IN_SECONDS = 3600; + public static final String CACHE_PROVIDER_CAFFEINE = "Caffeine"; public static final String CACHE_PROVIDER_REDIS = "Redis"; diff --git a/modules/deployment/pom.xml b/modules/deployment/pom.xml index f1a2a4c83..d1715113b 100644 --- a/modules/deployment/pom.xml +++ b/modules/deployment/pom.xml @@ -105,6 +105,16 @@ awaitility ${awaitility.version} + + org.semver4j + semver4j + ${semver4j.version} + + + org.kohsuke + github-api + ${github-api.version} + diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/DeployService.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/DeployService.java index d2f98848c..14afd7372 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/DeployService.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/DeployService.java @@ -6,6 +6,7 @@ package org.eclipse.xpanse.modules.deployment; +import static org.eclipse.xpanse.modules.async.TaskConfiguration.ASYNC_EXECUTOR_NAME; import static org.eclipse.xpanse.modules.logging.LoggingKeyConstant.SERVICE_ID; import jakarta.annotation.Resource; @@ -104,7 +105,7 @@ public class DeployService { private ServiceOrderManager serviceOrderManager; @Resource private ServiceDeploymentStatusChangePolling serviceDeploymentStatusChangePolling; - @Resource + @Resource(name = ASYNC_EXECUTOR_NAME) private Executor taskExecutor; diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceOrderManager.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceOrderManager.java index 3d286330e..163039015 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceOrderManager.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceOrderManager.java @@ -6,6 +6,7 @@ package org.eclipse.xpanse.modules.deployment; +import static org.eclipse.xpanse.modules.async.TaskConfiguration.ASYNC_EXECUTOR_NAME; import static org.eclipse.xpanse.modules.security.common.RoleConstants.ROLE_ADMIN; import jakarta.annotation.Resource; @@ -49,7 +50,7 @@ public class ServiceOrderManager { private UserServiceHelper userServiceHelper; @Resource private ServiceOrderStatusChangePolling serviceOrderStatusChangePolling; - @Resource + @Resource(name = ASYNC_EXECUTOR_NAME) private Executor taskExecutor; /** diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceStateManager.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceStateManager.java index f2b1b117f..92b38e6b1 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceStateManager.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/ServiceStateManager.java @@ -6,6 +6,7 @@ package org.eclipse.xpanse.modules.deployment; +import static org.eclipse.xpanse.modules.async.TaskConfiguration.ASYNC_EXECUTOR_NAME; import static org.eclipse.xpanse.modules.security.common.RoleConstants.ROLE_ADMIN; import jakarta.annotation.Resource; @@ -35,7 +36,6 @@ import org.eclipse.xpanse.modules.orchestrator.PluginManager; import org.eclipse.xpanse.modules.orchestrator.servicestate.ServiceStateManageRequest; import org.eclipse.xpanse.modules.security.UserServiceHelper; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -57,8 +57,7 @@ public class ServiceStateManager { private DatabaseServiceStateManagementTaskStorage taskStorage; @Resource private DatabaseDeployServiceStorage deployServiceStorage; - @Qualifier("xpanseAsyncTaskExecutor") - @Resource + @Resource(name = ASYNC_EXECUTOR_NAME) private Executor taskExecutor; /** diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolUtils.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolUtils.java new file mode 100644 index 000000000..010f3f020 --- /dev/null +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolUtils.java @@ -0,0 +1,430 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.deployment.deployers.deployertools; + +import jakarta.annotation.Resource; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.xpanse.common.systemcmd.SystemCmd; +import org.eclipse.xpanse.common.systemcmd.SystemCmdResult; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; +import org.eclipse.xpanse.modules.models.servicetemplate.DeployerTool; +import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; +import org.semver4j.Semver; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +/** + * Utils for DeployerTool. + */ +@Slf4j +@Component +public class DeployerToolUtils { + + private static final Pattern DEPLOYER_TOOL_REQUIRED_VERSION_PATTERN = + Pattern.compile(DeployerTool.DEPLOYER_TOOL_REQUIRED_VERSION_REGEX); + private static final String OS_NAME = System.getProperty("os.name").toLowerCase(); + private static final String OS_ARCH = System.getProperty("os.arch").toLowerCase(); + private final SystemCmd systemCmd = new SystemCmd(); + @Resource + private DeployerToolVersionsCacheManager versionsCacheManager; + + /** + * Get executor path which matches the required version. + * + * @param executorNamePrefix executor name prefix + * @param versionCommandOutputPattern pattern to get version from command output + * @param installationDir installation directory + * @param requiredOperator operator in required version + * @param requiredNumber number in required version + * @return return the version of which is matched required, otherwise return null. + */ + public String getExecutorPathMatchedRequiredVersion(String executorNamePrefix, + Pattern versionCommandOutputPattern, + String installationDir, + String requiredOperator, + String requiredNumber) { + // Get path of executor matched required version in the installation dir. + File installDir = new File(installationDir); + if (!installDir.exists() || !installDir.isDirectory()) { + return null; + } + Map executorVersionFileMap = new HashMap<>(); + Arrays.stream(installDir.listFiles()) + .filter(f -> f.isFile() && f.canExecute() + && f.getName().startsWith(executorNamePrefix)) + .forEach(f -> { + String versionNumber = + getVersionFromExecutorPath(f.getAbsolutePath(), executorNamePrefix); + executorVersionFileMap.put(versionNumber, f); + }); + if (CollectionUtils.isEmpty(executorVersionFileMap)) { + return null; + } + String findBestVersion = findBestVersion(executorVersionFileMap.keySet(), + requiredOperator, requiredNumber); + if (StringUtils.isNotBlank(findBestVersion)) { + File executorFile = executorVersionFileMap.get(findBestVersion); + if (checkIfExecutorVersionIsValid(executorFile, versionCommandOutputPattern, + requiredOperator, requiredNumber)) { + log.info("Found the installed executor {} matched the required version {} " + + "successfully.", executorFile.getAbsolutePath(), + requiredOperator + requiredNumber); + return executorFile.getAbsolutePath(); + } + } + return null; + } + + /** + * Install the executor with specific version into the path. + * + * @param executorNamePrefix executor name prefix + * @param versionNumber version number + * @param binaryDownloadUrlFormat binary download url format + * @param downloadBaseUrl download base url + * @param installationDir installation directory + * @return executor path + */ + public File installDeployerToolWithVersion(String executorNamePrefix, String versionNumber, + String binaryDownloadUrlFormat, + String downloadBaseUrl, String installationDir) { + // Install the executor with specific version into the path. + String executorName = getExecutorNameWithVersion(executorNamePrefix, versionNumber); + File executorFile = new File(installationDir, executorName); + File parentDir = executorFile.getParentFile(); + try { + if (!parentDir.exists()) { + log.info("Created the installation dir {} {}.", parentDir.getAbsolutePath(), + parentDir.mkdirs() ? "successfully" : "failed"); + } + // download the binary zip file from website into the installation directory + File terraformZipFile = + downloadDeployerToolBinaryZipFile(binaryDownloadUrlFormat, downloadBaseUrl, + versionNumber, installationDir); + // extract the executable binary from the zip file + extractExecutorFromZipFile(terraformZipFile, executorFile, executorNamePrefix); + } catch (IOException e) { + String errorMsg = + String.format("Failed to install deployer tool with version %s.", executorName); + log.error(errorMsg, e); + throw new InvalidDeployerToolException(errorMsg); + } + // delete the non-executable files + deleteNonExecutorFiles(parentDir, executorNamePrefix); + return executorFile; + } + + + private File downloadDeployerToolBinaryZipFile(String binaryDownloadUrlFormat, + String downloadBaseUrl, String versionNumber, + String installationDir) throws IOException { + String binaryDownloadUrl = getExecutorBinaryDownloadUrl( + binaryDownloadUrlFormat, downloadBaseUrl, versionNumber); + String binaryZipFileName = getExecutorBinaryZipFileName(binaryDownloadUrl); + File binaryZipFile = new File(installationDir, binaryZipFileName); + URL url = URI.create(binaryDownloadUrl).toURL(); + try (ReadableByteChannel rbc = Channels.newChannel(url.openStream()); + FileOutputStream fos = new FileOutputStream(binaryZipFile, false)) { + log.info("Downloading deployer tool binary file from {} to {}", url, + binaryZipFile.getAbsolutePath()); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + log.info("Downloaded deployer tool binary file from {} to {} successfully.", url, + binaryZipFile.getAbsolutePath()); + } + return binaryZipFile; + } + + + private void extractExecutorFromZipFile(File binaryZipFile, File executorFile, + String executorNamePrefix) throws IOException { + if (!binaryZipFile.exists()) { + String errorMsg = String.format("Deployer tool binary zip file %s not found.", + binaryZipFile.getAbsolutePath()); + log.error(errorMsg); + throw new IOException(errorMsg); + } + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(binaryZipFile))) { + log.info("Unzipping deployer tool binary zip file {}", binaryZipFile.getAbsolutePath()); + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if (!entry.isDirectory()) { + String entryName = entry.getName(); + File entryDestinationFile = new File(executorFile.getParentFile(), entryName); + if (isExecutorFileInZipForTerraform(entryName, executorNamePrefix)) { + extractFile(zis, entryDestinationFile); + Files.move(entryDestinationFile.toPath(), executorFile.toPath(), + StandardCopyOption.REPLACE_EXISTING); + log.info("Unzipped deployer tool file {} and extract the executor {} " + + "successfully.", binaryZipFile.getAbsolutePath(), + executorFile.getAbsolutePath()); + } + } + } + } + } + + private boolean isExecutorFileInZipForTerraform(String entryName, String executorNamePrefix) { + executorNamePrefix = executorNamePrefix.substring(0, executorNamePrefix.length() - 1); + return entryName.startsWith(executorNamePrefix); + } + + + private void extractFile(ZipInputStream zis, File destinationFile) throws IOException { + try (BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(destinationFile))) { + byte[] bytesIn = new byte[4096]; + int read; + while ((read = zis.read(bytesIn)) != -1) { + bos.write(bytesIn, 0, read); + } + } + } + + private void deleteNonExecutorFiles(File dir, String executorNamePrefix) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteNonExecutorFiles(file, executorNamePrefix); + } else { + if (!file.getName().startsWith(executorNamePrefix) && !file.delete()) { + log.warn("Failed to delete file {}.", file.getAbsolutePath()); + } + } + } + } + } + + /** + * Get the operator and number from the required version. + * + * @param requiredVersion required version + * @return string array, the first element is operator, the second element is number. + */ + public String[] getOperatorAndNumberFromRequiredVersion(String requiredVersion) { + + String version = requiredVersion.replaceAll("\\s+", "").toLowerCase().replaceAll("v", ""); + if (StringUtils.isNotBlank(version)) { + Matcher matcher = DEPLOYER_TOOL_REQUIRED_VERSION_PATTERN.matcher(version); + if (matcher.find()) { + String[] operatorAndNumber = new String[2]; + operatorAndNumber[0] = matcher.group(1); + operatorAndNumber[1] = matcher.group(0).replaceAll("^(=|>=|<=)", ""); + return operatorAndNumber; + } + } + String errorMsg = + String.format("Invalid deployer tool required version format:%s", requiredVersion); + throw new InvalidDeployerToolException(errorMsg); + } + + + /** + * Get the best available version to install executor with required version. + * + * @param requiredOperator operator in required version + * @param requiredNumber number in required version + * @return the best available version existed in download url. + */ + public String getBestAvailableVersionMatchingRequiredVersion(DeployerKind deployerKind, + String requiredOperator, + String requiredNumber) { + Set availableVersions = + versionsCacheManager.getAvailableVersionsOfDeployerTool(deployerKind); + String bestAvailableVersion = + findBestVersion(availableVersions, requiredOperator, requiredNumber); + if (StringUtils.isNotBlank(bestAvailableVersion)) { + log.info("Found the best available version {} for the deployer tool {} by the " + + "required version {}.", bestAvailableVersion, deployerKind.toValue(), + requiredOperator + requiredNumber); + return bestAvailableVersion; + } + String errorMsg = String.format("Failed to find available versions for the " + + "deployer tool %s by the required version %s.", + deployerKind.toValue(), requiredOperator + requiredNumber); + log.error(errorMsg); + throw new InvalidDeployerToolException(errorMsg); + } + + /** + * Find the best version from all available versions. + * + * @param allAvailableVersions all available versions. + * @param requiredOperator operator of the required version. + * @param requiredNumber number of the required version. + * @return the best version. + */ + private String findBestVersion(Set allAvailableVersions, String requiredOperator, + String requiredNumber) { + if (CollectionUtils.isEmpty(allAvailableVersions) || StringUtils.isBlank(requiredOperator) + || StringUtils.isBlank(requiredNumber)) { + return null; + } + Semver requiredSemver = new Semver(requiredNumber); + return switch (requiredOperator) { + case "=" -> allAvailableVersions.stream() + .filter(v -> new Semver(v).isEqualTo(requiredSemver)).findAny().orElse(null); + case ">=" -> allAvailableVersions.stream() + .filter(v -> new Semver(v).isGreaterThanOrEqualTo(requiredSemver)) + .min(Comparator.naturalOrder()).orElse(null); + case "<=" -> allAvailableVersions.stream() + .filter(v -> new Semver(v).isLowerThanOrEqualTo(requiredSemver)) + .max(Comparator.naturalOrder()).orElse(null); + default -> null; + }; + } + + /** + * Check if the exact version of executor is valid. + * + * @param executorFile executor file + * @param versionCommandOutputPattern pattern of the version command output + * @param requiredOperator operator in required version + * @param requiredNumber number in required version + * @return true if the version is valid, otherwise return false. + */ + public boolean checkIfExecutorVersionIsValid(File executorFile, + Pattern versionCommandOutputPattern, + String requiredOperator, String requiredNumber) { + if (!executorFile.exists() && !executorFile.isFile()) { + return false; + } + if (!executorFile.canExecute()) { + SystemCmdResult chmodResult = + systemCmd.execute(String.format("chmod +x %s", executorFile.getAbsolutePath())); + if (!chmodResult.isCommandSuccessful()) { + log.error(chmodResult.getCommandStdError()); + return false; + } + } + String actualVersion = getExactVersionOfExecutor(executorFile.getAbsolutePath(), + versionCommandOutputPattern); + if (StringUtils.isBlank(actualVersion)) { + return false; + } + return isVersionSatisfied(actualVersion, requiredOperator, requiredNumber); + } + + + /** + * Get the exact version of executor. + * + * @param executorPath the path of executor + * @param versionCommandOutputPattern pattern of the version command output + * @return the exact version of executor + */ + public String getExactVersionOfExecutor(String executorPath, + Pattern versionCommandOutputPattern) { + SystemCmdResult versionCheckResult = + systemCmd.execute(executorPath + " -v"); + if (versionCheckResult.isCommandSuccessful()) { + return getActualVersionFromCommandOutput(versionCheckResult.getCommandStdOutput(), + versionCommandOutputPattern); + } else { + return null; + } + } + + + private String getVersionFromExecutorPath(String executorPath, String executorNamePrefix) { + if (executorPath.contains(executorNamePrefix)) { + return Arrays.asList(executorPath.split(executorNamePrefix)).getLast(); + } + return null; + } + + private String getActualVersionFromCommandOutput(String commandOutput, + Pattern versionCommandOutputPattern) { + Matcher matcher = versionCommandOutputPattern.matcher(commandOutput); + if (matcher.find()) { + return matcher.group(1); + } + return null; + } + + private boolean isVersionSatisfied(String actualNumber, String requiredOperator, + String requiredNumber) { + Semver actualSemver = new Semver(actualNumber); + Semver requiredSemver = new Semver(requiredNumber); + if ("=".equals(requiredOperator)) { + return actualSemver.isEqualTo(requiredSemver); + } else if (">=".equals(requiredOperator)) { + return actualSemver.isGreaterThanOrEqualTo(requiredSemver); + } else if ("<=".equals(requiredOperator)) { + return actualSemver.isLowerThanOrEqualTo(requiredSemver); + } + return false; + } + + + /** + * Get executor name with version. + * + * @param versionNumber version number + * @return binary file name + */ + public String getExecutorNameWithVersion(String executorNamePrefix, String versionNumber) { + return executorNamePrefix + versionNumber; + } + + + /** + * Get whole download url of the executor binary file. + * + * @param binaryDownloadUrlFormat binary download url format + * @param downloadBaseUrl download base url + * @param versionNumber version number + * @return whole download url of the executor binary file + */ + private String getExecutorBinaryDownloadUrl(String binaryDownloadUrlFormat, + String downloadBaseUrl, String versionNumber) { + return String.format(binaryDownloadUrlFormat, downloadBaseUrl, versionNumber, versionNumber, + getOperatingSystemCode(), OS_ARCH); + } + + private String getExecutorBinaryZipFileName(String executorBinaryDownloadUrl) { + return executorBinaryDownloadUrl.substring(executorBinaryDownloadUrl.lastIndexOf("/") + 1); + } + + + private String getOperatingSystemCode() { + if (OS_NAME.contains("windows")) { + return "windows"; + } else if (OS_NAME.contains("linux")) { + return "linux"; + } else if (OS_NAME.contains("mac")) { + return "darwin"; + } else if (OS_NAME.contains("freebsd")) { + return "freebsd"; + } else if (OS_NAME.contains("openbsd")) { + return "openbsd"; + } else if (OS_NAME.contains("solaris") || OS_NAME.contains("sunos")) { + return "solaris"; + } + return "Unsupported OS"; + } +} diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCache.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCache.java new file mode 100644 index 000000000..8949119f0 --- /dev/null +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCache.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.deployment.deployers.deployertools; + + +import static org.eclipse.xpanse.modules.cache.consts.CacheConstants.DEPLOYER_VERSIONS_CACHE_NAME; + +import jakarta.annotation.Resource; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Component; + +/** + * Bean to update the cache of versions of OpenTofu. + */ +@Slf4j +@Component +public class DeployerToolVersionsCache { + + @Resource + private DeployerToolVersionsFetcher versionsFetcher; + + /** + * Get the available versions of OpenTofu. + * + * @return Set of available versions. + */ + @Cacheable(value = DEPLOYER_VERSIONS_CACHE_NAME, key = "#deployerKind") + public Set getVersionsCacheOfDeployerTool(DeployerKind deployerKind) { + try { + return versionsFetcher.fetchOfficialVersionsOfDeployerTool(deployerKind); + } catch (Exception e) { + return versionsFetcher.getVersionsFromDefaultConfigOfDeployerTool(deployerKind); + } + } + + /** + * Update the cache of versions of OpenTofu. + * + * @param versions List of available versions. + */ + @CachePut(value = DEPLOYER_VERSIONS_CACHE_NAME, key = "#deployerKind") + public void updateCachedVersionsOfDeployerTool(DeployerKind deployerKind, + Set versions) { + log.info("Updated versions cache of deployer:{} with versions:{}.", + deployerKind.toValue(), versions); + } + + + +} diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCacheManager.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCacheManager.java new file mode 100644 index 000000000..c1445a36e --- /dev/null +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsCacheManager.java @@ -0,0 +1,120 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.deployment.deployers.deployertools; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +/** + * Bean to update the cache of versions of Terraform. + */ +@Slf4j +@Component +public class DeployerToolVersionsCacheManager { + @Resource + private DeployerToolVersionsCache versionsCache; + @Resource + private DeployerToolVersionsFetcher versionsFetcher; + + /** + * Initialize the versions caches for all deployer tools. + */ + @PostConstruct + public void initializeCache() { + Arrays.stream(DeployerKind.values()).forEach(deployerKind -> { + Set versions = versionsCache.getVersionsCacheOfDeployerTool(deployerKind); + log.info("Initialized versions cache for deployer tool {} with versions {}.", + deployerKind.toValue(), versions); + }); + } + + /** + * Update the versions cache for all deployer tools fetched from their website. + * This method is scheduled run once a day. + */ + @Scheduled(cron = "0 0 1 * * ?") + public void fetchVersionsFromWebsiteAndLoadCache() { + Arrays.stream(DeployerKind.values()).forEach(deployerKind -> { + try { + fetchVersionsFromWebsiteAndLoadCacheForDeployerTool(deployerKind); + } catch (Exception e) { + log.error("Failed to fetch versions from website for deployer tool {}.", + deployerKind.toValue(), e); + } + }); + } + + /** + * Update the versions cache for all deployer tools fetched from their website when the cached + * versions are empty or the cached versions are the same as the default version list. + * This method is scheduled to run every one hour. + */ + @Scheduled(cron = "0 1 * * * ?") + public void fetchVersionsFromWebsiteAndLoadCacheIfCacheHasOnlyDefaultVersions() { + Arrays.stream(DeployerKind.values()).forEach(deployerKind -> { + try { + Set cachedVersions = + versionsCache.getVersionsCacheOfDeployerTool(deployerKind); + Set defaultVersions = + versionsFetcher.getVersionsFromDefaultConfigOfDeployerTool(deployerKind); + if (CollectionUtils.isEmpty(cachedVersions) + || Objects.equals(cachedVersions, defaultVersions)) { + fetchVersionsFromWebsiteAndLoadCacheForDeployerTool(deployerKind); + } + } catch (Exception e) { + log.error("Failed to fetch versions from website for deployer tool {}.", + deployerKind.toValue(), e); + } + }); + } + + /** + * Get the available versions cache of deployer tool. when the cached versions is empty, or + * the cached versions is the same as the default versions, then fetch the versions from the + * website of the deployer tool, and update the cache. + * + * @param deployerKind The kind of deployer tool. + * @return The available versions cache of deployer tool. + */ + public Set getAvailableVersionsOfDeployerTool(DeployerKind deployerKind) { + Set cachedVersions = + versionsCache.getVersionsCacheOfDeployerTool(deployerKind); + Set defaultVersions = + versionsFetcher.getVersionsFromDefaultConfigOfDeployerTool(deployerKind); + if (CollectionUtils.isEmpty(cachedVersions) + || Objects.equals(cachedVersions, defaultVersions)) { + try { + return fetchVersionsFromWebsiteAndLoadCacheForDeployerTool(deployerKind); + } catch (Exception e) { + log.error("Failed to fetch versions from website for deployer tool {}.", + deployerKind.toValue(), e); + return defaultVersions; + } + } + return cachedVersions; + } + + + private Set fetchVersionsFromWebsiteAndLoadCacheForDeployerTool( + DeployerKind deployerKind) throws Exception { + Set availableVersionsFromWebsite = + versionsFetcher.fetchOfficialVersionsOfDeployerTool(deployerKind); + versionsCache.updateCachedVersionsOfDeployerTool(deployerKind, + availableVersionsFromWebsite); + log.info("Successfully updated versions cache for deployer tool {} with versions " + + "{}.", deployerKind.toValue(), availableVersionsFromWebsite); + return availableVersionsFromWebsite; + } + +} diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsFetcher.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsFetcher.java new file mode 100644 index 000000000..23e0a68c6 --- /dev/null +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/deployertools/DeployerToolVersionsFetcher.java @@ -0,0 +1,137 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.deployment.deployers.deployertools; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.xpanse.modules.models.common.exceptions.ClientApiCallFailedException; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; +import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GHTag; +import org.kohsuke.github.GitHub; +import org.kohsuke.github.GitHubBuilder; +import org.kohsuke.github.GitHubRateLimitHandler; +import org.kohsuke.github.PagedIterable; +import org.kohsuke.github.connector.GitHubConnectorResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Component; + +/** + * Beans for fetching all available versions of the deployer tool. + */ +@Slf4j +@Component +public class DeployerToolVersionsFetcher { + + private static final Pattern OFFICIAL_VERSION_PATTERN = + Pattern.compile("^v(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})$"); + @Value("${deployer.terraform.github.api.endpoint:https//api.github.com}") + private String terraformGithubApiEndpoint; + @Value("${deployer.terraform.github.repository:hashicorp/terraform}") + private String terraformGithubRepository; + @Value("${deployer.terraform.versions}") + private String terraformDefaultVersionsStr; + + @Value("${deployer.opentofu.github.api.endpoint:https//api.github.com}") + private String openTofuGithubApiEndpoint; + @Value("${deployer.opentofu.github.repository:opentofu/opentofu}") + private String openTofuGithubRepository; + @Value("${deployer.opentofu.versions}") + private String openTofuDefaultVersionsStr; + + + /** + * Fetch all available versions from the website of deployer. + * + * @return all available versions. + */ + @Retryable(retryFor = Exception.class, + maxAttemptsExpression = "${http.request.retry.max.attempts}", + backoff = @Backoff(delayExpression = "${http.request.retry.delay.milliseconds}")) + public Set fetchOfficialVersionsOfDeployerTool(DeployerKind deployerKind) + throws Exception { + Set allVersions = new HashSet<>(); + String apiEndpoint = getGithubApiEndpoint(deployerKind); + String githubRepository = getGithubRepository(deployerKind); + GitHub gitHub = new GitHubBuilder() + .withEndpoint(apiEndpoint) + .withRateLimitHandler(getGithubRateLimitHandler()) + .build(); + GHRepository repository = gitHub.getRepository(githubRepository); + PagedIterable tags = repository.listTags(); + tags.forEach(tag -> { + String version = tag.getName(); + if (OFFICIAL_VERSION_PATTERN.matcher(version).matches()) { + // remove the prefix 'v' + allVersions.add(version.substring(1)); + } + }); + log.info("Get available versions {} from website for deployer tool {}.", allVersions, + deployerKind.toValue()); + if (allVersions.isEmpty()) { + String errorMsg = String.format("No available versions found from website for deployer " + + "tool %s", deployerKind.toValue()); + throw new InvalidDeployerToolException(errorMsg); + } + return allVersions; + } + + /** + * Get default versions from config. + * + * @return default versions. + */ + public Set getVersionsFromDefaultConfigOfDeployerTool(DeployerKind deployerKind) { + String defaultVersionsString = getDefaultVersionsString(deployerKind); + Set defaultVersions = + Set.of(defaultVersionsString.replaceAll("//s+", "").split(",")); + log.info("Get versions {} from default config value {} for deployer tool {}", + defaultVersions, defaultVersionsString, deployerKind.toValue()); + return defaultVersions; + } + + + private String getGithubApiEndpoint(DeployerKind deployerKind) { + return switch (deployerKind) { + case TERRAFORM -> terraformGithubApiEndpoint; + case OPEN_TOFU -> openTofuGithubApiEndpoint; + }; + } + + private String getGithubRepository(DeployerKind deployerKind) { + return switch (deployerKind) { + case TERRAFORM -> terraformGithubRepository; + case OPEN_TOFU -> openTofuGithubRepository; + }; + } + + private String getDefaultVersionsString(DeployerKind deployerKind) { + return switch (deployerKind) { + case TERRAFORM -> terraformDefaultVersionsStr; + case OPEN_TOFU -> openTofuDefaultVersionsStr; + }; + } + + private GitHubRateLimitHandler getGithubRateLimitHandler() { + return new GitHubRateLimitHandler() { + @Override + public void onError(@Nonnull GitHubConnectorResponse response) { + String limit = response.header("X-RateLimit-Limit"); + String remaining = response.header("X-RateLimit-Remaining"); + String reset = response.header("X-RateLimit-Reset"); + String errorMsg = String.format("GitHub API rate limit exceeded. " + + "Rate limit: %s, remaining: %s, reset time: %s", limit, remaining, reset); + throw new ClientApiCallFailedException(errorMsg); + } + }; + } +} diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuInstaller.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuInstaller.java new file mode 100644 index 000000000..fdd96bebe --- /dev/null +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuInstaller.java @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.deployment.deployers.opentofu.opentofulocal; + +import jakarta.annotation.Resource; +import java.io.File; +import java.util.regex.Pattern; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.xpanse.modules.deployment.deployers.deployertools.DeployerToolUtils; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; +import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Component; + +/** + * Defines methods for handling open tofu with required version. + */ +@Slf4j +@Component +public class OpenTofuInstaller { + + /** + * The pattern of the output of the command tofu -v. + */ + public static final Pattern OPEN_TOFU_VERSION_OUTPUT_PATTERN = + Pattern.compile("^OpenTofu\\s+v(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\b"); + private static final String OPEN_TOFU_BINARY_DOWNLOAD_URL_FORMAT = + "%s/download/v%s/tofu_%s_%s_%s.zip"; + private static final String OPEN_TOFU_EXECUTOR_NAME_PREFIX = "tofu-"; + + @Value("${deployer.opontofu.download.base.url:https://github.com/opentofu/opentofu/releases}") + private String openTofuDownloadBaseUrl; + + @Value("${deployer.opontofu.install.dir:/opt/opentofu}") + private String openTofuInstallDir; + + @Resource + private DeployerToolUtils deployerToolUtils; + + + /** + * Find the executable binary path of the Terraform tool that matches the required version. + * If no matching executable binary is found, install the Terraform tool with the required + * version and then return the path. + * + * @param requiredVersion The required version of Terraform tool. + * @return The path of the executable binary. + */ + @Retryable(retryFor = InvalidDeployerToolException.class, + maxAttemptsExpression = "${http.request.retry.max.attempts}", + backoff = @Backoff(delayExpression = "${http.request.retry.delay.milliseconds}")) + public String getExecutorPathThatMatchesRequiredVersion(String requiredVersion) { + if (StringUtils.isBlank(requiredVersion)) { + log.info("No required version of openTofu is specified, use the default openTofu."); + return "tofu"; + } + String[] operatorAndNumber = + deployerToolUtils.getOperatorAndNumberFromRequiredVersion(requiredVersion); + String requiredOperator = operatorAndNumber[0]; + String requiredNumber = operatorAndNumber[1]; + // Get path of the executor matched required version in the environment. + String matchedVersionExecutorPath = deployerToolUtils.getExecutorPathMatchedRequiredVersion( + OPEN_TOFU_EXECUTOR_NAME_PREFIX, OPEN_TOFU_VERSION_OUTPUT_PATTERN, + this.openTofuInstallDir, requiredOperator, requiredNumber); + if (StringUtils.isBlank(matchedVersionExecutorPath)) { + log.info("Not found any openTofu executor matched the required version {} from the " + + "openTofu installation dir {}, start to download and install one.", + requiredVersion, this.openTofuInstallDir); + return installOpenTofuByRequiredVersion(requiredOperator, requiredNumber); + } + return matchedVersionExecutorPath; + } + + private String installOpenTofuByRequiredVersion(String requiredOperator, + String requiredNumber) { + String bestVersionNumber = deployerToolUtils.getBestAvailableVersionMatchingRequiredVersion( + DeployerKind.OPEN_TOFU, requiredOperator, requiredNumber); + File installedExecutorFile = + deployerToolUtils.installDeployerToolWithVersion(OPEN_TOFU_EXECUTOR_NAME_PREFIX, + bestVersionNumber, OPEN_TOFU_BINARY_DOWNLOAD_URL_FORMAT, + this.openTofuDownloadBaseUrl, this.openTofuInstallDir); + if (deployerToolUtils.checkIfExecutorVersionIsValid(installedExecutorFile, + OPEN_TOFU_VERSION_OUTPUT_PATTERN, requiredOperator, requiredNumber)) { + log.info("OpenTofu with version {} installed successfully.", installedExecutorFile); + return installedExecutorFile.getAbsolutePath(); + } + String errorMsg = String.format("Installing openTofu with version %s into the dir %s " + + "failed. ", bestVersionNumber, this.openTofuInstallDir); + log.error(errorMsg); + throw new InvalidDeployerToolException(errorMsg); + } + + +} diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeployment.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeployment.java index 5a3ffaa40..e46855cb6 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeployment.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeployment.java @@ -6,7 +6,10 @@ package org.eclipse.xpanse.modules.deployment.deployers.opentofu.opentofulocal; +import static org.eclipse.xpanse.modules.async.TaskConfiguration.ASYNC_EXECUTOR_NAME; + import jakarta.annotation.Nullable; +import jakarta.annotation.Resource; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -41,8 +44,6 @@ import org.eclipse.xpanse.modules.orchestrator.deployment.DeployTask; import org.eclipse.xpanse.modules.orchestrator.deployment.Deployer; import org.eclipse.xpanse.modules.orchestrator.deployment.DeploymentScriptValidationResult; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Component; @@ -56,34 +57,23 @@ public class OpenTofuLocalDeployment implements Deployer { public static final String SCRIPT_FILE_NAME = "resources.tf"; public static final String STATE_FILE_NAME = "terraform.tfstate"; public static final String TF_DEBUG_FLAG = "TF_LOG"; - private final DeployEnvironments deployEnvironments; - private final OpenTofuLocalConfig openTofuLocalConfig; - private final Executor taskExecutor; - private final OpenTofuDeploymentResultCallbackManager openTofuDeploymentResultCallbackManager; - private final DeployServiceEntityHandler deployServiceEntityHandler; - private final ScriptsGitRepoManage scriptsGitRepoManage; - private final DeployResultFileUtils deployResultFileUtils; - /** - * Initializes the OpenTofu deployer. - */ - @Autowired - public OpenTofuLocalDeployment(DeployEnvironments deployEnvironments, - OpenTofuLocalConfig openTofuLocalConfig, - @Qualifier("xpanseAsyncTaskExecutor") Executor taskExecutor, - OpenTofuDeploymentResultCallbackManager - openTofuDeploymentResultCallbackManager, - DeployServiceEntityHandler deployServiceEntityHandler, - ScriptsGitRepoManage scriptsGitRepoManage, - DeployResultFileUtils deployResultFileUtils) { - this.deployEnvironments = deployEnvironments; - this.openTofuLocalConfig = openTofuLocalConfig; - this.taskExecutor = taskExecutor; - this.openTofuDeploymentResultCallbackManager = openTofuDeploymentResultCallbackManager; - this.deployServiceEntityHandler = deployServiceEntityHandler; - this.scriptsGitRepoManage = scriptsGitRepoManage; - this.deployResultFileUtils = deployResultFileUtils; - } + @Resource + private OpenTofuInstaller openTofuInstaller; + @Resource + private DeployEnvironments deployEnvironments; + @Resource + private OpenTofuLocalConfig openTofuLocalConfig; + @Resource(name = ASYNC_EXECUTOR_NAME) + private Executor taskExecutor; + @Resource + private OpenTofuDeploymentResultCallbackManager openTofuDeploymentResultCallbackManager; + @Resource + private DeployServiceEntityHandler deployServiceEntityHandler; + @Resource + private ScriptsGitRepoManage scriptsGitRepoManage; + @Resource + private DeployResultFileUtils deployResultFileUtils; /** * Deploy the DeployTask. @@ -290,7 +280,9 @@ private OpenTofuLocalExecutor getExecutor(Map envVariables, openTofuLocalConfig.getDebugLogLevel()); envVariables.put(TF_DEBUG_FLAG, openTofuLocalConfig.getDebugLogLevel()); } - return new OpenTofuLocalExecutor(envVariables, inputVariables, workspace, + String executorPath = openTofuInstaller.getExecutorPathThatMatchesRequiredVersion( + deployment.getDeployerTool().getVersion()); + return new OpenTofuLocalExecutor(executorPath, envVariables, inputVariables, workspace, getSubDirectory(deployment), deployResultFileUtils); } diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutor.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutor.java index 8635f21e4..5f48fc421 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutor.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutor.java @@ -41,6 +41,7 @@ public class OpenTofuLocalExecutor { OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); } + private final String executorPath; private final Map env; private final Map variables; private final String workspace; @@ -49,16 +50,19 @@ public class OpenTofuLocalExecutor { /** * Constructor for openTofuExecutor. * + * @param executorPath path of the open tofu executor. * @param env environment for the open tofu command line. * @param variables variables for the open tofu command line. * @param workspace workspace for the open tofu command line. * @param deployResultFileUtils file tool class. */ - OpenTofuLocalExecutor(Map env, + OpenTofuLocalExecutor(String executorPath, + Map env, Map variables, String workspace, @Nullable String subDirectory, DeployResultFileUtils deployResultFileUtils) { + this.executorPath = executorPath; this.env = env; this.variables = variables; this.workspace = @@ -74,7 +78,7 @@ public class OpenTofuLocalExecutor { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfInit() { - return execute("tofu init -no-color"); + return execute(this.executorPath + " init -no-color"); } /** @@ -83,7 +87,8 @@ public SystemCmdResult tfInit() { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfPlan() { - return executeWithVariables(new StringBuilder("tofu plan -input=false -no-color ")); + return executeWithVariables( + new StringBuilder(this.executorPath + " plan -input=false -no-color ")); } /** @@ -93,7 +98,7 @@ public SystemCmdResult tfPlan() { */ public SystemCmdResult tfPlanWithOutput() { return executeWithVariables(new StringBuilder( - "tofu plan -input=false -no-color --out tfplan.binary")); + this.executorPath + " plan -input=false -no-color --out tfplan.binary")); } /** @@ -102,8 +107,8 @@ public SystemCmdResult tfPlanWithOutput() { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfApply() { - return executeWithVariables( - new StringBuilder("tofu apply -auto-approve -input=false -no-color ")); + return executeWithVariables(new StringBuilder( + this.executorPath + " apply -auto-approve -input=false -no-color ")); } /** @@ -112,8 +117,8 @@ public SystemCmdResult tfApply() { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfDestroy() { - return executeWithVariables( - new StringBuilder("tofu destroy -auto-approve -input=false -no-color ")); + return executeWithVariables(new StringBuilder( + this.executorPath + " destroy -auto-approve -input=false -no-color ")); } /** @@ -248,7 +253,7 @@ public String getOpenTofuPlanAsJson() { throw new OpenTofuExecutorException("OpenTofuExecutor.tfPlan failed.", tfPlanResult.getCommandStdError()); } - SystemCmdResult planJsonResult = execute("tofu show -json tfplan.binary"); + SystemCmdResult planJsonResult = execute(this.executorPath + " show -json tfplan.binary"); if (!planJsonResult.isCommandSuccessful()) { log.error("Reading OpenTofu plan as JSON failed."); throw new OpenTofuExecutorException("Reading OpenTofu plan as JSON failed.", @@ -286,7 +291,7 @@ public DeploymentScriptValidationResult tfValidate() { throw new OpenTofuExecutorException("OpenTofuExecutor.tfInit failed.", initResult.getCommandStdError()); } - SystemCmdResult validateResult = execute("tofu validate -json -no-color"); + SystemCmdResult validateResult = execute(this.executorPath + " validate -json -no-color"); if (!validateResult.isCommandSuccessful()) { log.error("OpenTofuExecutor get validate json failed."); diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformInstaller.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformInstaller.java new file mode 100644 index 000000000..abfcc3b68 --- /dev/null +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformInstaller.java @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal; + +import jakarta.annotation.Resource; +import java.io.File; +import java.util.regex.Pattern; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.xpanse.modules.deployment.deployers.deployertools.DeployerToolUtils; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; +import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Component; + +/** + * Defines methods for handling terraform with required version. + */ +@Slf4j +@Component +public class TerraformInstaller { + + /** + * The pattern of the output of the command terraform -v. + */ + public static final Pattern TERRAFORM_VERSION_OUTPUT_PATTERN = + Pattern.compile("^Terraform\\s+v(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\b"); + private static final String TERRAFORM_BINARY_DOWNLOAD_URL_FORMAT = + "%s/%s/terraform_%s_%s_%s.zip"; + private static final String TERRAFORM_EXECUTOR_NAME_PREFIX = "terraform-"; + + @Value("${deployer.terraform.download.base.url:https://releases.hashicorp.com/terraform}") + private String terraformDownloadBaseUrl; + + @Value("${deployer.terraform.install.dir:/opt/terraform}") + private String terraformInstallDir; + @Resource + private DeployerToolUtils deployerToolUtils; + + + /** + * Find the executable binary path of the Terraform tool that matches the required version. + * If no matching executable binary is found, install the Terraform tool with the required + * version and then return the path. + * + * @param requiredVersion The required version of Terraform tool. + * @return The path of the executable binary. + */ + @Retryable(retryFor = InvalidDeployerToolException.class, + maxAttemptsExpression = "${http.request.retry.max.attempts}", + backoff = @Backoff(delayExpression = "${http.request.retry.delay.milliseconds}")) + public String getExecutorPathThatMatchesRequiredVersion(String requiredVersion) { + if (StringUtils.isBlank(requiredVersion)) { + log.info("No required version of terraform is specified, use the default terraform."); + return "terraform"; + } + String[] operatorAndNumber = + deployerToolUtils.getOperatorAndNumberFromRequiredVersion(requiredVersion); + String requiredOperator = operatorAndNumber[0]; + String requiredNumber = operatorAndNumber[1]; + // Get path of the executor matched required version in the environment. + String matchedVersionExecutorPath = deployerToolUtils.getExecutorPathMatchedRequiredVersion( + TERRAFORM_EXECUTOR_NAME_PREFIX, TERRAFORM_VERSION_OUTPUT_PATTERN, + this.terraformInstallDir, requiredOperator, requiredNumber); + if (StringUtils.isBlank(matchedVersionExecutorPath)) { + log.info("Not found any terraform executor matched the required version {} from the " + + "terraform installation dir {}, start to download and install one.", + requiredVersion, this.terraformInstallDir); + return installTerraformByRequiredVersion(requiredOperator, requiredNumber); + } + return matchedVersionExecutorPath; + } + + private String installTerraformByRequiredVersion(String requiredOperator, + String requiredNumber) { + String bestVersionNumber = deployerToolUtils.getBestAvailableVersionMatchingRequiredVersion( + DeployerKind.TERRAFORM, requiredOperator, requiredNumber); + File installedExecutorFile = + deployerToolUtils.installDeployerToolWithVersion(TERRAFORM_EXECUTOR_NAME_PREFIX, + bestVersionNumber, TERRAFORM_BINARY_DOWNLOAD_URL_FORMAT, + this.terraformDownloadBaseUrl, this.terraformInstallDir); + if (deployerToolUtils.checkIfExecutorVersionIsValid(installedExecutorFile, + TERRAFORM_VERSION_OUTPUT_PATTERN, requiredOperator, requiredNumber)) { + log.info("Terraform with version {} installed successfully.", installedExecutorFile); + return installedExecutorFile.getAbsolutePath(); + } + String errorMsg = String.format("Installing terraform with version %s into the dir %s " + + "failed. ", bestVersionNumber, this.terraformInstallDir); + log.error(errorMsg); + throw new InvalidDeployerToolException(errorMsg); + } +} diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeployment.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeployment.java index fb20e8839..d3058936e 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeployment.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeployment.java @@ -5,7 +5,10 @@ package org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal; +import static org.eclipse.xpanse.modules.async.TaskConfiguration.ASYNC_EXECUTOR_NAME; + import jakarta.annotation.Nullable; +import jakarta.annotation.Resource; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -40,8 +43,6 @@ import org.eclipse.xpanse.modules.orchestrator.deployment.DeployTask; import org.eclipse.xpanse.modules.orchestrator.deployment.Deployer; import org.eclipse.xpanse.modules.orchestrator.deployment.DeploymentScriptValidationResult; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Component; @@ -55,34 +56,23 @@ public class TerraformLocalDeployment implements Deployer { public static final String SCRIPT_FILE_NAME = "resources.tf"; public static final String STATE_FILE_NAME = "terraform.tfstate"; public static final String TF_DEBUG_FLAG = "TF_LOG"; - private final DeployEnvironments deployEnvironments; - private final TerraformLocalConfig terraformLocalConfig; - private final Executor taskExecutor; - private final TerraformDeploymentResultCallbackManager terraformDeploymentResultCallbackManager; - private final DeployServiceEntityHandler deployServiceEntityHandler; - private final ScriptsGitRepoManage scriptsGitRepoManage; - private final DeployResultFileUtils deployResultFileUtils; - /** - * Initializes the Terraform deployer. - */ - @Autowired - public TerraformLocalDeployment(DeployEnvironments deployEnvironments, - TerraformLocalConfig terraformLocalConfig, - @Qualifier("xpanseAsyncTaskExecutor") Executor taskExecutor, - TerraformDeploymentResultCallbackManager - terraformDeploymentResultCallbackManager, - DeployServiceEntityHandler deployServiceEntityHandler, - ScriptsGitRepoManage scriptsGitRepoManage, - DeployResultFileUtils deployResultFileUtils) { - this.deployEnvironments = deployEnvironments; - this.terraformLocalConfig = terraformLocalConfig; - this.taskExecutor = taskExecutor; - this.terraformDeploymentResultCallbackManager = terraformDeploymentResultCallbackManager; - this.deployServiceEntityHandler = deployServiceEntityHandler; - this.scriptsGitRepoManage = scriptsGitRepoManage; - this.deployResultFileUtils = deployResultFileUtils; - } + @Resource + private TerraformInstaller terraformInstaller; + @Resource + private DeployEnvironments deployEnvironments; + @Resource + private TerraformLocalConfig terraformLocalConfig; + @Resource(name = ASYNC_EXECUTOR_NAME) + private Executor taskExecutor; + @Resource + private TerraformDeploymentResultCallbackManager terraformDeploymentResultCallbackManager; + @Resource + private DeployServiceEntityHandler deployServiceEntityHandler; + @Resource + private ScriptsGitRepoManage scriptsGitRepoManage; + @Resource + private DeployResultFileUtils deployResultFileUtils; /** * Deploy the DeployTask. @@ -288,7 +278,9 @@ private TerraformLocalExecutor getExecutor(Map envVariables, terraformLocalConfig.getDebugLogLevel()); envVariables.put(TF_DEBUG_FLAG, terraformLocalConfig.getDebugLogLevel()); } - return new TerraformLocalExecutor(envVariables, inputVariables, workspace, + String executorPath = terraformInstaller.getExecutorPathThatMatchesRequiredVersion( + deployment.getDeployerTool().getVersion()); + return new TerraformLocalExecutor(executorPath, envVariables, inputVariables, workspace, getSubDirectory(deployment), deployResultFileUtils); } diff --git a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutor.java b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutor.java index 7ba2ab29b..9f59dea0f 100644 --- a/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutor.java +++ b/modules/deployment/src/main/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutor.java @@ -40,6 +40,7 @@ public class TerraformLocalExecutor { OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); } + private final String executorPath; private final Map env; private final Map variables; private final String workspace; @@ -48,16 +49,19 @@ public class TerraformLocalExecutor { /** * Constructor for terraformExecutor. * + * @param executorPath path of the terraform executor. * @param env environment for the terraform command line. * @param variables variables for the terraform command line. * @param workspace workspace for the terraform command line. * @param deployResultFileUtils file tool class. */ - TerraformLocalExecutor(Map env, + TerraformLocalExecutor(String executorPath, + Map env, Map variables, String workspace, @Nullable String subDirectory, DeployResultFileUtils deployResultFileUtils) { + this.executorPath = executorPath; this.env = env; this.variables = variables; this.workspace = @@ -73,7 +77,7 @@ public class TerraformLocalExecutor { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfInit() { - return execute("terraform init -no-color"); + return execute(this.executorPath + " init -no-color"); } /** @@ -82,7 +86,8 @@ public SystemCmdResult tfInit() { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfPlan() { - return executeWithVariables(new StringBuilder("terraform plan -input=false -no-color ")); + return executeWithVariables( + new StringBuilder(this.executorPath + " plan -input=false -no-color ")); } /** @@ -92,7 +97,7 @@ public SystemCmdResult tfPlan() { */ public SystemCmdResult tfPlanWithOutput() { return executeWithVariables(new StringBuilder( - "terraform plan -input=false -no-color --out tfplan.binary")); + this.executorPath + " plan -input=false -no-color --out tfplan.binary")); } /** @@ -101,8 +106,8 @@ public SystemCmdResult tfPlanWithOutput() { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfApply() { - return executeWithVariables( - new StringBuilder("terraform apply -auto-approve -input=false -no-color ")); + return executeWithVariables(new StringBuilder( + this.executorPath + " apply -auto-approve -input=false -no-color ")); } /** @@ -111,8 +116,8 @@ public SystemCmdResult tfApply() { * @return Returns result of SystemCmd executed. */ public SystemCmdResult tfDestroy() { - return executeWithVariables( - new StringBuilder("terraform destroy -auto-approve -input=false -no-color ")); + return executeWithVariables(new StringBuilder( + this.executorPath + " destroy -auto-approve -input=false -no-color ")); } /** @@ -190,7 +195,7 @@ public void destroy() { } /** - * Reads the contents of the "terraform.tfstate" file from the terraform workspace. + * Reads the contents of the this.executorPath + ".tfstate" file from the terraform workspace. * * @return file contents as string. */ @@ -247,7 +252,7 @@ public String getTerraformPlanAsJson() { throw new TerraformExecutorException("TFExecutor.tfPlan failed.", tfPlanResult.getCommandStdError()); } - SystemCmdResult planJsonResult = execute("terraform show -json tfplan.binary"); + SystemCmdResult planJsonResult = execute(this.executorPath + " show -json tfplan.binary"); if (!planJsonResult.isCommandSuccessful()) { log.error("Reading Terraform plan as JSON failed."); throw new TerraformExecutorException("Reading Terraform plan as JSON failed.", @@ -285,7 +290,7 @@ public DeploymentScriptValidationResult tfValidate() { throw new TerraformExecutorException("TFExecutor.tfInit failed.", initResult.getCommandStdError()); } - SystemCmdResult systemCmdResult = execute("terraform validate -json -no-color"); + SystemCmdResult systemCmdResult = execute(this.executorPath + " validate -json -no-color"); try { return new ObjectMapper().readValue(systemCmdResult.getCommandStdOutput(), DeploymentScriptValidationResult.class); diff --git a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeploymentTest.java b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeploymentTest.java index 130c35794..0e0426125 100644 --- a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeploymentTest.java +++ b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalDeploymentTest.java @@ -9,7 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal.TerraformLocalDeployment.STATE_FILE_NAME; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import java.io.File; @@ -25,18 +24,15 @@ import java.util.concurrent.Executor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.eclipse.xpanse.modules.async.TaskConfiguration; import org.eclipse.xpanse.modules.database.service.DeployServiceEntity; import org.eclipse.xpanse.modules.deployment.DeployService; import org.eclipse.xpanse.modules.deployment.DeployServiceEntityHandler; -import org.eclipse.xpanse.modules.deployment.ResourceHandlerManager; import org.eclipse.xpanse.modules.deployment.deployers.opentofu.callbacks.OpenTofuDeploymentResultCallbackManager; import org.eclipse.xpanse.modules.deployment.deployers.opentofu.exceptions.OpenTofuExecutorException; import org.eclipse.xpanse.modules.deployment.deployers.opentofu.opentofulocal.config.OpenTofuLocalConfig; import org.eclipse.xpanse.modules.deployment.deployers.opentofu.utils.TfResourceTransUtils; import org.eclipse.xpanse.modules.deployment.utils.DeployEnvironments; import org.eclipse.xpanse.modules.deployment.utils.DeployResultFileUtils; -import org.eclipse.xpanse.modules.deployment.utils.ScriptsGitRepoManage; import org.eclipse.xpanse.modules.models.service.deploy.DeployRequest; import org.eclipse.xpanse.modules.models.service.enums.DeployerTaskStatus; import org.eclipse.xpanse.modules.models.service.order.enums.ServiceOrderType; @@ -54,22 +50,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.io.ClassPathResource; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; /** * Test for OpenTofuDeployment. */ @Slf4j -@ExtendWith({SpringExtension.class}) -@ContextConfiguration(classes = {OpenTofuLocalDeployment.class, DeployEnvironments.class, - PluginManager.class, OpenTofuLocalConfig.class, DeployService.class, - TaskConfiguration.class, ResourceHandlerManager.class, ScriptsGitRepoManage.class}) +@ExtendWith({MockitoExtension.class}) class OpenTofuLocalDeploymentTest { private final String errorDeployer = "error_deployer"; @@ -82,30 +74,31 @@ class OpenTofuLocalDeploymentTest { value = resource.random_id_2.new.id } """; - @Autowired + @InjectMocks OpenTofuLocalDeployment openTofuLocalDeployment; - @MockBean + @Mock + OpenTofuInstaller openTofuInstaller; + @Mock DeployResultFileUtils deployResultFileUtils; - @MockBean + @Mock DeployEnvironments deployEnvironments; - @MockBean + @Mock OpenTofuLocalConfig openTofuLocalConfig; - @MockBean + @Mock PluginManager pluginManager; - @MockBean + @Mock DeployService deployService; - @MockBean + @Mock Executor taskExecutor; - @MockBean + @Mock OpenTofuDeploymentResultCallbackManager openTofuDeploymentResultCallbackManager; - @MockBean + @Mock DeployServiceEntityHandler deployServiceEntityHandler; private Ocl ocl; private Ocl oclWithGitScripts; @BeforeEach void setUp() throws Exception { - when(openTofuLocalConfig.getWorkspaceDirectory()).thenReturn("tofu-ws-test"); OclLoader oclLoader = new OclLoader(); ocl = oclLoader.getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); @@ -114,7 +107,6 @@ void setUp() throws Exception { oclWithGitScripts = oclLoader.getOcl( URI.create("file:src/test/resources/ocl_terraform_from_git_test.yml").toURL()); oclWithGitScripts.getDeployment().getDeployerTool().setKind(DeployerKind.OPEN_TOFU); - doReturn(new HashMap<>()).when(this.deployEnvironments).getEnvironmentVariables(any()); } DeployTask getDeployTask(Ocl ocl, ServiceOrderType serviceOrderType) { @@ -171,6 +163,7 @@ String getFileContent() { @Test void testDeploy() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.DEPLOY); deployTask.setDeploymentScenario(DeploymentScenario.DEPLOY); DeployResult deployResult = openTofuLocalDeployment.deploy(deployTask); @@ -196,6 +189,7 @@ void testDeploy() { @Test void testModify() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.MODIFY); String tfState = getFileContent(); DeployServiceEntity deployServiceEntity = new DeployServiceEntity(); @@ -227,6 +221,7 @@ void testModify() { @Test void testDestroy() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); String tfState = getFileContent(); DeployServiceEntity deployServiceEntity = new DeployServiceEntity(); deployServiceEntity.setPrivateProperties(Map.of(STATE_FILE_NAME, tfState)); @@ -261,6 +256,7 @@ void testDeleteTaskWorkspace() { @Test void testDeploy_FailedCausedByOpenTofuExecutorException() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); ocl.getDeployment().setDeployer(invalidDeployer); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.DEPLOY); DeployResult deployResult = this.openTofuLocalDeployment.deploy(deployTask); @@ -272,6 +268,7 @@ void testDeploy_FailedCausedByOpenTofuExecutorException() { @Test void testModify_FailedCausedByOpenTofuExecutorException() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); try (MockedStatic tfResourceTransUtils = Mockito.mockStatic( TfResourceTransUtils.class)) { tfResourceTransUtils.when(() -> TfResourceTransUtils.getStoredStateContent(any())) @@ -292,6 +289,7 @@ void testModify_FailedCausedByOpenTofuExecutorException() { @Test void testDestroy_FailedCausedByOpenTofuExecutorException() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); try (MockedStatic tfResourceTransUtils = Mockito.mockStatic( TfResourceTransUtils.class)) { tfResourceTransUtils.when(() -> TfResourceTransUtils.getStoredStateContent(any())) @@ -314,7 +312,7 @@ void testGetDeployerKind() { @Test void testGetDeployPlanAsJson() { - + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.DEPLOY); String deployPlanJson = openTofuLocalDeployment.getDeploymentPlanAsJson(deployTask); Assertions.assertNotNull(deployPlanJson); @@ -331,7 +329,7 @@ void testGetDeployPlanAsJson() { @Test void testGetDeployPlanAsJson_ThrowsException() { - + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); ocl.getDeployment().setDeployer(errorDeployer); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.DEPLOY); Assertions.assertThrows(OpenTofuExecutorException.class, @@ -341,6 +339,7 @@ void testGetDeployPlanAsJson_ThrowsException() { @Test void testValidate() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); DeploymentScriptValidationResult expectedResult = new DeploymentScriptValidationResult(); expectedResult.setValid(true); expectedResult.setDiagnostics(new ArrayList<>()); @@ -364,6 +363,7 @@ void testValidate() { @Test void testValidateFailed() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); ocl.getDeployment().setDeployer(invalidDeployer); DeploymentScriptValidationResult expectedResult = new DeploymentScriptValidationResult(); @@ -381,6 +381,7 @@ void testValidateFailed() { @Test void testValidate_ThrowsTerraformExecutorException() { + when(openTofuInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("tofu"); ocl.getDeployment().setDeployer(errorDeployer); Assertions.assertThrows(OpenTofuExecutorException.class, () -> openTofuLocalDeployment.validate(ocl.getDeployment())); diff --git a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutorTest.java b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutorTest.java index 24950f90c..947ad0aa1 100644 --- a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutorTest.java +++ b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/opentofu/opentofulocal/OpenTofuLocalExecutorTest.java @@ -46,7 +46,7 @@ class OpenTofuLocalExecutorTest { static void initWorkSpace() throws Exception { OclLoader oclLoader = new OclLoader(); Ocl ocl = oclLoader.getOcl( - URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); + URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); ocl.getDeployment().getDeployerTool().setKind(DeployerKind.OPEN_TOFU); String script = ocl.getDeployment().getDeployer(); File ws = new File(workspace + "/" + UUID.randomUUID()); @@ -59,8 +59,8 @@ static void initWorkSpace() throws Exception { @BeforeEach void setUp() { - openTofuLocalExecutorUnderTest = new OpenTofuLocalExecutor(mockEnv, mockVariables, - workspace, null, deployResultFileUtilsTest); + openTofuLocalExecutorUnderTest = new OpenTofuLocalExecutor( + "tofu", mockEnv, mockVariables, workspace, null, deployResultFileUtilsTest); } @Test diff --git a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeploymentTest.java b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeploymentTest.java index 6f91566bc..289ddfe4a 100644 --- a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeploymentTest.java +++ b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalDeploymentTest.java @@ -9,7 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal.TerraformLocalDeployment.STATE_FILE_NAME; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import java.io.File; @@ -25,18 +24,14 @@ import java.util.concurrent.Executor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; -import org.eclipse.xpanse.modules.async.TaskConfiguration; import org.eclipse.xpanse.modules.database.service.DeployServiceEntity; import org.eclipse.xpanse.modules.deployment.DeployService; import org.eclipse.xpanse.modules.deployment.DeployServiceEntityHandler; -import org.eclipse.xpanse.modules.deployment.ResourceHandlerManager; -import org.eclipse.xpanse.modules.deployment.deployers.terraform.callbacks.TerraformDeploymentResultCallbackManager; import org.eclipse.xpanse.modules.deployment.deployers.terraform.exceptions.TerraformExecutorException; import org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal.config.TerraformLocalConfig; import org.eclipse.xpanse.modules.deployment.deployers.terraform.utils.TfResourceTransUtils; import org.eclipse.xpanse.modules.deployment.utils.DeployEnvironments; import org.eclipse.xpanse.modules.deployment.utils.DeployResultFileUtils; -import org.eclipse.xpanse.modules.deployment.utils.ScriptsGitRepoManage; import org.eclipse.xpanse.modules.models.service.deploy.DeployRequest; import org.eclipse.xpanse.modules.models.service.enums.DeployerTaskStatus; import org.eclipse.xpanse.modules.models.service.order.enums.ServiceOrderType; @@ -54,22 +49,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.io.ClassPathResource; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; /** * Test for TerraformDeployment. */ @Slf4j -@ExtendWith({SpringExtension.class}) -@ContextConfiguration(classes = {TerraformLocalDeployment.class, DeployEnvironments.class, - PluginManager.class, TerraformLocalConfig.class, DeployService.class, - TaskConfiguration.class, ResourceHandlerManager.class, ScriptsGitRepoManage.class}) +@ExtendWith({MockitoExtension.class}) class TerraformLocalDeploymentTest { private final String errorDeployer = "error_deployer"; @@ -82,38 +73,35 @@ class TerraformLocalDeploymentTest { value = resource.random_id_2.new.id } """; - @Autowired + @InjectMocks TerraformLocalDeployment terraformLocalDeployment; - @MockBean + @Mock DeployResultFileUtils deployResultFileUtils; - @MockBean + @Mock DeployEnvironments deployEnvironments; - @MockBean + @Mock TerraformLocalConfig terraformLocalConfig; - @MockBean + @Mock PluginManager pluginManager; - @MockBean + @Mock DeployService deployService; - @MockBean + @Mock Executor taskExecutor; - @MockBean - TerraformDeploymentResultCallbackManager terraformDeploymentResultCallbackManager; - @MockBean + @Mock DeployServiceEntityHandler deployServiceEntityHandler; + @Mock + TerraformInstaller terraformInstaller; private Ocl ocl; private Ocl oclWithGitScripts; @BeforeEach void setUp() throws Exception { - when(terraformLocalConfig.getWorkspaceDirectory()).thenReturn("tf-ws-test"); OclLoader oclLoader = new OclLoader(); ocl = oclLoader.getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); - oclWithGitScripts = oclLoader.getOcl( URI.create("file:src/test/resources/ocl_terraform_from_git_test.yml").toURL()); - doReturn(new HashMap<>()).when(this.deployEnvironments).getEnvironmentVariables(any()); } DeployTask getDeployTask(Ocl ocl, ServiceOrderType taskType) { @@ -272,6 +260,7 @@ void testDeploy_FailedCausedByTerraformExecutorException() { @Test void testDestroy_FailedCausedByTerraformExecutorException() { + when(terraformInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("terraform"); try (MockedStatic tfResourceTransUtils = Mockito.mockStatic( TfResourceTransUtils.class)) { tfResourceTransUtils.when(() -> TfResourceTransUtils.getStoredStateContent(any())) @@ -295,11 +284,11 @@ void testGetDeployerKind() { @Test void testGetDeployPlanAsJson() { + when(terraformInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("terraform"); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.DEPLOY); deployTask.setDeploymentScenario(DeploymentScenario.DEPLOY); String deployPlanJson = terraformLocalDeployment.getDeploymentPlanAsJson(deployTask); Assertions.assertNotNull(deployPlanJson); - try { DeployTask deployTask1 = getDeployTask(oclWithGitScripts, ServiceOrderType.DEPLOY); String deployPlanJson1 = terraformLocalDeployment.getDeploymentPlanAsJson(deployTask1); @@ -311,6 +300,7 @@ void testGetDeployPlanAsJson() { @Test void testGetDeployPlanAsJson_ThrowsException() { + when(terraformInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("terraform"); ocl.getDeployment().setDeployer(errorDeployer); DeployTask deployTask = getDeployTask(ocl, ServiceOrderType.DEPLOY); Assertions.assertThrows(TerraformExecutorException.class, @@ -319,6 +309,7 @@ void testGetDeployPlanAsJson_ThrowsException() { @Test void testValidate() { + when(terraformInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("terraform"); DeploymentScriptValidationResult expectedResult = new DeploymentScriptValidationResult(); expectedResult.setValid(true); expectedResult.setDiagnostics(new ArrayList<>()); @@ -342,6 +333,7 @@ void testValidate() { @Test void testValidateFailed() { + when(terraformInstaller.getExecutorPathThatMatchesRequiredVersion(any())).thenReturn("terraform"); ocl.getDeployment().setDeployer(invalidDeployer); DeploymentScriptValidationResult expectedResult = new DeploymentScriptValidationResult(); diff --git a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutorTest.java b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutorTest.java index 48eccc8d2..9bd3c9a97 100644 --- a/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutorTest.java +++ b/modules/deployment/src/test/java/org/eclipse/xpanse/modules/deployment/deployers/terraform/terraformlocal/TerraformLocalExecutorTest.java @@ -69,7 +69,7 @@ static void initWorkSpace() throws Exception { @BeforeEach void setUp() { - terraformLocalExecutorUnderTest = new TerraformLocalExecutor( + terraformLocalExecutorUnderTest = new TerraformLocalExecutor("terraform", mockEnv, mockVariables, workspace, null, deployResultFileUtilsTest); } diff --git a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/common/exceptions/InvalidDeployerToolException.java b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/common/exceptions/InvalidDeployerToolException.java new file mode 100644 index 000000000..8cbabf5e9 --- /dev/null +++ b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/common/exceptions/InvalidDeployerToolException.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * SPDX-FileCopyrightText: Huawei Inc. + */ + +package org.eclipse.xpanse.modules.models.common.exceptions; + +/** + * Defines exception when the deployer tool is invalid or installed failed. + */ +public class InvalidDeployerToolException extends RuntimeException { + public InvalidDeployerToolException(String message) { + super(message); + } +} diff --git a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/response/ResultType.java b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/response/ResultType.java index c7c3e0d16..8ec69ac81 100644 --- a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/response/ResultType.java +++ b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/response/ResultType.java @@ -76,7 +76,8 @@ public enum ResultType { FILE_LOCKED("File Locked"), INVALID_SERVICE_CONFIGURATION("Service Configuration Invalid"), SERVICE_CONFIG_UPDATE_REQUEST_NOT_FOUND("Service Configuration Update Request Not Found"), - SERVICE_CONFIGURATION_NOT_FOUND("Service Configuration Not Found"); + SERVICE_CONFIGURATION_NOT_FOUND("Service Configuration Not Found"), + INVALID_DEPLOYER_TOOL("Invalid Deployer Tool"); private final String value; diff --git a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/DeployerTool.java b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/DeployerTool.java index 4ccc190ab..a3a14f5c2 100644 --- a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/DeployerTool.java +++ b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/DeployerTool.java @@ -5,12 +5,13 @@ package org.eclipse.xpanse.modules.models.servicetemplate; +import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import lombok.Data; import org.eclipse.xpanse.modules.models.servicetemplate.enums.DeployerKind; -import org.eclipse.xpanse.modules.models.servicetemplate.validators.DeployerVersionConstraint; import org.springframework.validation.annotation.Validated; /** @@ -19,6 +20,12 @@ @Data @Validated public class DeployerTool { + /** + * Deployer version required version regex. + */ + @Hidden + public static final String DEPLOYER_TOOL_REQUIRED_VERSION_REGEX = + "^(=|>=|<=)\\s*[vV]?\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$"; @NotNull @Schema(description = "The type of the deployer which will handle the service deployment.") @@ -26,7 +33,7 @@ public class DeployerTool { @NotNull @NotBlank - @DeployerVersionConstraint + @Pattern(regexp = DEPLOYER_TOOL_REQUIRED_VERSION_REGEX) @Schema(description = "The version of the deployer which will handle the service deployment.") private String version; } diff --git a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionConstraint.java b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionConstraint.java deleted file mode 100644 index 1babf9a58..000000000 --- a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionConstraint.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * SPDX-FileCopyrightText: Huawei Inc. - */ - -package org.eclipse.xpanse.modules.models.servicetemplate.validators; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Bean validation for deployer version. - */ -@Documented -@Constraint(validatedBy = DeployerVersionValidator.class) -@Target({ElementType.METHOD, ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface DeployerVersionConstraint { - - /** - * Message for invalid deployer version. - */ - String message() default "Invalid deployer version."; - - /** - * Classes which the constraint applies to. - */ - Class[] groups() default {}; - - /** - * Payload for the constraint. - */ - Class[] payload() default {}; -} diff --git a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionValidator.java b/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionValidator.java deleted file mode 100644 index 785276546..000000000 --- a/modules/models/src/main/java/org/eclipse/xpanse/modules/models/servicetemplate/validators/DeployerVersionValidator.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * SPDX-FileCopyrightText: Huawei Inc. - */ - -package org.eclipse.xpanse.modules.models.servicetemplate.validators; - -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import java.util.regex.Pattern; -import org.apache.commons.lang3.StringUtils; - -/** - * Validator for Deployer version. - */ -public class DeployerVersionValidator - implements ConstraintValidator { - - - /** - * Regular expression for matching deployer version. - * This Pattern object is used to validate strings against a specific version number format. - * The version number format includes: =, >=, <=, followed by optional space and v/V, - * and then three numeric parts separated by dots. - */ - private static final Pattern VERSION_PATTERN = - Pattern.compile("^(=|>=|<=)\\s*([vV])?\\d+\\.\\d+\\.\\d+$"); - - @Override - public void initialize(DeployerVersionConstraint constraintAnnotation) { - // No initialization required - } - - @Override - public boolean isValid(String version, ConstraintValidatorContext context) { - if (StringUtils.isBlank(version)) { - return false; - } - return VERSION_PATTERN.matcher(version).matches(); - } -} diff --git a/pom.xml b/pom.xml index 38199de36..fcf46aa31 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,7 @@ 5.1.0 7.0.0.202409031743-r 5.4.0 + 1.326 1.4 2.1.22 2.2.25 @@ -159,6 +160,11 @@ + + javax.annotation + javax.annotation-api + 1.3.2 + org.jacoco org.jacoco.core diff --git a/runtime/src/main/resources/application-test.properties b/runtime/src/main/resources/application-test.properties index 43143ff25..3a3013ba4 100644 --- a/runtime/src/main/resources/application-test.properties +++ b/runtime/src/main/resources/application-test.properties @@ -3,7 +3,10 @@ # SPDX-FileCopyrightText: Huawei Inc. # # -openstack.endpoint="https://119.8.97.198:5000/v3" -openstack.secret="openstack" -openstack.domainName="Default" -openstack.enableSslCertificateValidation="false" \ No newline at end of file +http.logging.enabled=false +OPENSTACK_TESTLAB_AUTH_URL=http://127.0.0.1/identity/v3 +PLUS_SERVER_AUTH_URL=http://127.0.0.1/identity/v3 +REGIO_CLOUD_AUTH_URL=http://127.0.0.1/identity/v3 +http.request.retry.max.attempts=1 +http.request.retry.delay.milliseconds=1000 +OS_AUTH_URL=http://127.0.0.1/v3/identity \ No newline at end of file diff --git a/runtime/src/main/resources/application.properties b/runtime/src/main/resources/application.properties index 36382db93..49ac8a0d6 100644 --- a/runtime/src/main/resources/application.properties +++ b/runtime/src/main/resources/application.properties @@ -69,4 +69,14 @@ huaweicloud.international.price.calculator.url=https://portal-intl.huaweicloud.c huaweicloud.european.price.calculator.url=https://portal.eu.huaweicloud.com/api/cbc/global/rest/BSS/billing/ratingservice/v2/inquiry/resource huaweicloud.chinese.price.calculator.url=https://portal.huaweicloud.com/api/cbc/global/rest/BSS/billing/ratingservice/v2/inquiry/resource enable.agent.api.only=false -agent.api.end.point=http://localhost:8080 \ No newline at end of file +agent.api.end.point=http://localhost:8080 +deployer.terraform.install.dir=/opt/terraform +deployer.terraform.versions=1.6.0,1.7.0,1.8.0,1.9.0 +deployer.terraform.github.api.endpoint=https://api.github.com +deployer.terraform.github.repository=hashicorp/terraform +deployer.terraform.download.base.url=https://releases.hashicorp.com/terraform +deployer.opontofu.install.dir=/opt/opentofu +deployer.opentofu.versions=1.6.0,1.7.0,1.8.0 +deployer.opentofu.github.api.endpoint=https://api.github.com +deployer.opentofu.github.repository=opentofu/opentofu +deployer.opontofu.download.base.url=https://github.com/opentofu/opentofu/releases \ No newline at end of file diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/AdminServicesApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/AdminServicesApiTest.java index e0af05ec6..0d8abbab6 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/AdminServicesApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/AdminServicesApiTest.java @@ -38,8 +38,7 @@ */ @Slf4j @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"enable.redis.distributed.cache=false", - "spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,tofu-maker,opentelemetry"}) +@SpringBootTest(properties = "spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,tofu-maker,opentelemetry,test") @AutoConfigureMockMvc class AdminServicesApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ArchitectureTests.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ArchitectureTests.java index 68a2a0a4b..2967b2970 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ArchitectureTests.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ArchitectureTests.java @@ -76,6 +76,7 @@ public static void cacheClassLocation(JavaClasses classes) { ArchRule archRule = classes() .that() .haveSimpleNameContaining("Cache") + .and().haveSimpleNameNotContaining("DeployerTool") .should() .resideInAPackage("..cache.."); archRule.check(classes); diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/AuthorizationApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/AuthorizationApiTest.java index 0fdce7a3e..46a2a44d0 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/AuthorizationApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/AuthorizationApiTest.java @@ -40,7 +40,7 @@ import org.springframework.web.client.RestTemplate; @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class AuthorizationApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/CredentialsConfigApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/CredentialsConfigApiTest.java index c546f1591..f47ad7ece 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/CredentialsConfigApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/CredentialsConfigApiTest.java @@ -37,7 +37,7 @@ @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class CredentialsConfigApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/CspServiceTemplateApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/CspServiceTemplateApiTest.java index d0dcca6b8..dacff02e7 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/CspServiceTemplateApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/CspServiceTemplateApiTest.java @@ -38,7 +38,7 @@ @Slf4j @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class CspServiceTemplateApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ExistingCloudResourcesApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ExistingCloudResourcesApiTest.java index 6bbd4d055..9284bfde6 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ExistingCloudResourcesApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ExistingCloudResourcesApiTest.java @@ -67,9 +67,7 @@ @SuppressWarnings("unchecked") @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed", - "http.request.retry.max.attempts=5", - "http.request.retry.delay.milliseconds=1000"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ExistingCloudResourcesApiTest extends ApisTestCommon { @@ -422,7 +420,7 @@ void testGetExistingResourceNamesWithKindForFlexibleEngine() throws Exception { assertThat(flexibleEngineVmResponse.getContentAsString()).isEqualTo( objectMapper.writeValueAsString(flexibleEngineVmResult)); - deleteCredential(Csp.FLEXIBLE_ENGINE, site,CredentialType.VARIABLES, "AK_SK"); + deleteCredential(Csp.FLEXIBLE_ENGINE, site, CredentialType.VARIABLES, "AK_SK"); } void testGetExistingResourceNamesWithKindForCspBasedOpenstack() throws Exception { @@ -629,7 +627,7 @@ void testGetExistingResourceNamesWithKindForCspWithOsClient(Csp csp) throws Exce assertThat(openstackVmResponse.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(openstackVmResponse.getContentAsString()).isEqualTo("[]"); - deleteCredential(csp, site,CredentialType.VARIABLES, "USERNAME_PASSWORD"); + deleteCredential(csp, site, CredentialType.VARIABLES, "USERNAME_PASSWORD"); } diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvCloudCredentialsApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvCloudCredentialsApiTest.java index f0ac1543a..e6c0d04d8 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvCloudCredentialsApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvCloudCredentialsApiTest.java @@ -37,7 +37,7 @@ */ @ExtendWith(SpringExtension.class) @Transactional -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class IsvCloudCredentialsApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvServiceDeployerApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvServiceDeployerApiTest.java index 8beb850ed..86f589f1c 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvServiceDeployerApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/IsvServiceDeployerApiTest.java @@ -55,7 +55,7 @@ */ @Slf4j @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class IsvServiceDeployerApiTest extends ApisTestCommon { void addIsvCredential() throws Exception { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/OpenTofuMakerWebhookApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/OpenTofuMakerWebhookApiTest.java index dc3d11152..be0066ff3 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/OpenTofuMakerWebhookApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/OpenTofuMakerWebhookApiTest.java @@ -57,7 +57,7 @@ @Slf4j @ExtendWith(SpringExtension.class) @CrossOrigin -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,tofu-maker"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,tofu-maker,test"}) @AutoConfigureMockMvc public class OpenTofuMakerWebhookApiTest extends ApisTestCommon { @@ -173,13 +173,11 @@ void testOpenTofuBootWebhookApisWell() throws Exception { addCredentialForHuaweiCloud(); Ocl ocl = new OclLoader().getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); - ocl.setName("OpenTofuMakerWebhookApiTest-1"); ocl.getDeployment().getDeployerTool().setKind(DeployerKind.OPEN_TOFU); testCallbackApiWithOcl(ocl); Ocl oclFromGit = new OclLoader().getOcl( URI.create("file:src/test/resources/ocl_terraform_from_git_test.yml").toURL()); - oclFromGit.setName("OpenTofuMakerWebhookApiTest-2"); oclFromGit.getDeployment().getDeployerTool().setKind(DeployerKind.OPEN_TOFU); testCallbackApiWithOcl(oclFromGit); } diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceCatalogApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceCatalogApiTest.java index 409f167ff..58e117c54 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceCatalogApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceCatalogApiTest.java @@ -52,7 +52,7 @@ @Slf4j @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceCatalogApiTest extends ApisTestCommon { @@ -64,7 +64,6 @@ class ServiceCatalogApiTest extends ApisTestCommon { void testServiceCatalogServices() throws Exception { Ocl ocl = new OclLoader().getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); - ocl.setName("serviceCatalogApiTest-1"); ServiceTemplateDetailVo serviceTemplate = registerServiceTemplate(ocl); waitUntilServiceTemplateFilesAreFullyGenerated( serviceTemplate.getServiceTemplateId().toString()); diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceConfigurationApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceConfigurationApiTest.java index ae7c7e0dd..37bde83df 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceConfigurationApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceConfigurationApiTest.java @@ -33,8 +33,8 @@ import org.eclipse.xpanse.modules.models.service.enums.ServiceDeploymentState; import org.eclipse.xpanse.modules.models.service.order.ServiceOrder; import org.eclipse.xpanse.modules.models.service.statemanagement.enums.ServiceState; -import org.eclipse.xpanse.modules.models.serviceconfiguration.ServiceConfigurationUpdate; import org.eclipse.xpanse.modules.models.serviceconfiguration.ServiceConfigurationChangeOrderDetails; +import org.eclipse.xpanse.modules.models.serviceconfiguration.ServiceConfigurationUpdate; import org.eclipse.xpanse.modules.models.serviceconfiguration.enums.ServiceConfigurationStatus; import org.eclipse.xpanse.modules.models.servicetemplate.Ocl; import org.eclipse.xpanse.modules.models.servicetemplate.Region; @@ -56,9 +56,8 @@ /** * Test for ServiceConfigurationApi. */ -@SuppressWarnings("unchecked") @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceConfigurationApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceDeployerApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceDeployerApiTest.java index dd284baf4..8a8c0df56 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceDeployerApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceDeployerApiTest.java @@ -91,12 +91,7 @@ */ @SuppressWarnings("unchecked") @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = { - "spring.profiles.active=oauth,zitadel,zitadel-testbed", - "http.request.retry.max.attempts=2", - "http.request.retry.delay.milliseconds=1000", - "OS_AUTH_URL=http://127.0.0.1/v3/identity" -}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test",}) @AutoConfigureMockMvc class ServiceDeployerApiTest extends ApisTestCommon { @MockBean diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMetricsApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMetricsApiTest.java index dae4b16a0..39f18dee5 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMetricsApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMetricsApiTest.java @@ -34,7 +34,7 @@ @Transactional @ExtendWith(SpringExtension.class) @SpringBootTest(classes = {XpanseApplication.class, ServiceMetricsApi.class}, - properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) + properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceMetricsApiTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMigrationApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMigrationApiTest.java index 94eef7c35..01a0b75ee 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMigrationApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceMigrationApiTest.java @@ -33,7 +33,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceMigrationApiTest extends ApisTestCommon { @@ -43,7 +43,6 @@ void testServiceMigrationApis() throws Exception { // prepare data Ocl ocl = new OclLoader().getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); - ocl.setName("ServiceMigrationApiTest-1"); ServiceTemplateDetailVo serviceTemplate = registerServiceTemplate(ocl); approveServiceTemplateRegistration(serviceTemplate.getServiceTemplateId()); addCredentialForHuaweiCloud(); diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceOrderManageApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceOrderManageApiTest.java index 0470148d5..0f4ad621a 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceOrderManageApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceOrderManageApiTest.java @@ -48,7 +48,7 @@ */ @Slf4j @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceOrderManageApiTest extends ApisTestCommon { @Test @@ -57,7 +57,6 @@ void testServiceOrderManageApis() throws Exception { // Setup Ocl ocl = new OclLoader().getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); - ocl.setName("ServiceOrderManageApiTest"); ServiceTemplateDetailVo serviceTemplate = registerServiceTemplate(ocl); if (Objects.isNull(serviceTemplate)) { log.error("Register service template failed."); diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePolicyManageApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePolicyManageApiTest.java index 26e29180e..15fab5e7e 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePolicyManageApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePolicyManageApiTest.java @@ -48,7 +48,7 @@ */ @Slf4j @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServicePolicyManageApiTest extends ApisTestCommon { @@ -68,7 +68,6 @@ void mockPoliciesValidateRequest(boolean valid) { void testServicePoliciesManageApis() throws Exception { Ocl ocl = new OclLoader().getOcl( URI.create("file:src/test/resources/ocl_terraform_test.yml").toURL()); - ocl.setName("ServicePolicyManageApi"); ServiceTemplateDetailVo serviceTemplate = registerServiceTemplate(ocl); testServicePoliciesManageApisWell(serviceTemplate); testServicePoliciesManage_ThrowsExceptions(serviceTemplate); diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePricingApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePricingApiTest.java index 89abcd298..1b493e4ec 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePricingApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServicePricingApiTest.java @@ -49,7 +49,7 @@ @Slf4j @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServicePricingApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceStateManageApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceStateManageApiTest.java index 2d76c2592..60a4355cc 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceStateManageApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceStateManageApiTest.java @@ -72,12 +72,7 @@ @SuppressWarnings("unchecked") @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = { - "spring.profiles.active=oauth,zitadel,zitadel-testbed", - "http.request.retry.max.attempts=5", - "http.request.retry.delay.milliseconds=1000", - "OS_AUTH_URL=http://127.0.0.1/v3/identity" -}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceStateManageApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceTemplateApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceTemplateApiTest.java index 79262dd89..87dd70b0c 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceTemplateApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/ServiceTemplateApiTest.java @@ -70,7 +70,7 @@ @Slf4j @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class ServiceTemplateApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/TerraformBootWebhookApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/TerraformBootWebhookApiTest.java index fdbc80df7..7abe1c989 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/TerraformBootWebhookApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/TerraformBootWebhookApiTest.java @@ -56,8 +56,7 @@ @Slf4j @ExtendWith(SpringExtension.class) @CrossOrigin -@SpringBootTest(properties = - {"spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,test"}) @AutoConfigureMockMvc public class TerraformBootWebhookApiTest extends ApisTestCommon { @MockBean diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/UserCloudCredentialsApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/UserCloudCredentialsApiTest.java index 53f96066d..295df6802 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/UserCloudCredentialsApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/UserCloudCredentialsApiTest.java @@ -40,7 +40,7 @@ */ @ExtendWith(SpringExtension.class) @Transactional -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class UserCloudCredentialsApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/UserPolicyManageApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/UserPolicyManageApiTest.java index 2311c8c23..440a2db5b 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/UserPolicyManageApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/UserPolicyManageApiTest.java @@ -46,7 +46,7 @@ @Slf4j @Transactional @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class UserPolicyManageApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/WorkFlowApiTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/WorkFlowApiTest.java index 5fd4ac57a..63b176ff5 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/WorkFlowApiTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/WorkFlowApiTest.java @@ -28,7 +28,7 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(classes = {XpanseApplication.class, WorkFlowApiTest.class, WorkflowUtils.class}, - properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed"}) + properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test"}) @AutoConfigureMockMvc class WorkFlowApiTest extends ApisTestCommon { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/AdminHealthCheckWithRedisCacheTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/AdminHealthCheckWithRedisCacheTest.java index 359edbc39..f8568d023 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/AdminHealthCheckWithRedisCacheTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/AdminHealthCheckWithRedisCacheTest.java @@ -31,7 +31,7 @@ * Test for Health Check with Redis Cache and Database h2. */ @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed", +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test", "enable.redis.distributed.cache=true"}) @AutoConfigureMockMvc class AdminHealthCheckWithRedisCacheTest extends AbstractRedisIntegrationTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/UserCredentialsWithRedisTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/UserCredentialsWithRedisTest.java index cacd541ec..d5a0177fb 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/UserCredentialsWithRedisTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/cache/redis/UserCredentialsWithRedisTest.java @@ -31,7 +31,7 @@ * Test for UserCloudCredentialsApi with Redis cache. */ @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed", +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,test", "enable.redis.distributed.cache=true"}) @AutoConfigureMockMvc class UserCredentialsWithRedisTest extends AbstractRedisIntegrationTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/AdminHealthCheckWithMysqlDbTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/AdminHealthCheckWithMysqlDbTest.java index 148c34952..ba0a9585c 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/AdminHealthCheckWithMysqlDbTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/AdminHealthCheckWithMysqlDbTest.java @@ -35,8 +35,7 @@ @Slf4j @Testcontainers @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,mysql", - "enable.redis.distributed.cache=false"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,mysql,test"}) @AutoConfigureMockMvc class AdminHealthCheckWithMysqlDbTest extends AbstractMysqlIntegrationTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/DeploymentWithMysqlTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/DeploymentWithMysqlTest.java index a8bdc62b4..7e057e161 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/DeploymentWithMysqlTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/DeploymentWithMysqlTest.java @@ -61,7 +61,7 @@ @Slf4j @Testcontainers @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,mysql"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,mysql,test"}) @AutoConfigureMockMvc class DeploymentWithMysqlTest extends AbstractMysqlIntegrationTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/RegistrationWithMysqlTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/RegistrationWithMysqlTest.java index 0627a2f79..f4cf8e60e 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/RegistrationWithMysqlTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/database/mysql/RegistrationWithMysqlTest.java @@ -30,7 +30,7 @@ @Slf4j @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,mysql"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,mysql,test"}) @AutoConfigureMockMvc class RegistrationWithMysqlTest extends AbstractMysqlIntegrationTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/common/openapi/OpenApiCommonTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/common/openapi/OpenApiCommonTest.java index 1f5a3e7ff..b0dc03f03 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/common/openapi/OpenApiCommonTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/common/openapi/OpenApiCommonTest.java @@ -26,7 +26,7 @@ @ContextConfiguration(classes = {OpenApiUrlManage.class, OpenApiGeneratorJarManage.class}) @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = {"spring.profiles.active=default"}) +@SpringBootTest(properties = {"spring.profiles.active=test"}) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class OpenApiCommonTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/credential/CredentialOpenApiGeneratorTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/credential/CredentialOpenApiGeneratorTest.java index 8b934024a..12c96132c 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/credential/CredentialOpenApiGeneratorTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/credential/CredentialOpenApiGeneratorTest.java @@ -30,7 +30,7 @@ @SpringBootTest(classes = {XpanseApplication.class, CredentialsStore.class, ServletUriComponentsBuilder.class, OpenApiUrlManage.class, PluginManager.class}, properties = { - "spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,tofu-maker"}) + "spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,tofu-maker,test"}) class CredentialOpenApiGeneratorTest { @Autowired diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/OpenTofuInstallerTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/OpenTofuInstallerTest.java new file mode 100644 index 000000000..8840c941f --- /dev/null +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/OpenTofuInstallerTest.java @@ -0,0 +1,59 @@ +package org.eclipse.xpanse.runtime.modules.deployment; + +import static org.eclipse.xpanse.modules.deployment.deployers.opentofu.opentofulocal.OpenTofuInstaller.OPEN_TOFU_VERSION_OUTPUT_PATTERN; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.annotation.Resource; +import java.io.File; +import org.eclipse.xpanse.modules.deployment.deployers.deployertools.DeployerToolUtils; +import org.eclipse.xpanse.modules.deployment.deployers.opentofu.opentofulocal.OpenTofuInstaller; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(properties = {"http.request.retry.max.attempts=1"}) +class OpenTofuInstallerTest { + + @Resource + private OpenTofuInstaller installer; + @Resource + private DeployerToolUtils deployerToolUtils; + + @Test + void testGetExecutableTerraformByVersion() { + + String requiredVersion = ""; + String terraformPath = installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion); + assertEquals("tofu", terraformPath); + + String requiredVersion1 = "<= v1.7.0"; + String[] operatorAndNumber1 = deployerToolUtils.getOperatorAndNumberFromRequiredVersion( + requiredVersion1); + String terraformPath1 = + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion1); + assertTrue(deployerToolUtils.checkIfExecutorVersionIsValid(new File(terraformPath1), + OPEN_TOFU_VERSION_OUTPUT_PATTERN, operatorAndNumber1[0], operatorAndNumber1[1])); + + String requiredVersion2 = "= 1.6.0"; + String[] operatorAndNumber2 = + deployerToolUtils.getOperatorAndNumberFromRequiredVersion(requiredVersion2); + String terraformPath2 = + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion2); + assertTrue(deployerToolUtils.checkIfExecutorVersionIsValid(new File(terraformPath2), + OPEN_TOFU_VERSION_OUTPUT_PATTERN, operatorAndNumber2[0], operatorAndNumber2[1])); + + String requiredVersion3 = ">= v1.8.0"; + String[] operatorAndNumber3 = deployerToolUtils.getOperatorAndNumberFromRequiredVersion( + requiredVersion3); + String terraformPath3 = + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion3); + assertTrue(deployerToolUtils.checkIfExecutorVersionIsValid(new File(terraformPath3), + OPEN_TOFU_VERSION_OUTPUT_PATTERN, operatorAndNumber3[0], operatorAndNumber3[1])); + + String requiredVersion4 = ">= 100.0.0"; + assertThrows(InvalidDeployerToolException.class, () -> + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion4)); + } +} \ No newline at end of file diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/TerraformInstallerTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/TerraformInstallerTest.java new file mode 100644 index 000000000..d0a9760ed --- /dev/null +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/deployment/TerraformInstallerTest.java @@ -0,0 +1,63 @@ +package org.eclipse.xpanse.runtime.modules.deployment; + +import static org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal.TerraformInstaller.TERRAFORM_VERSION_OUTPUT_PATTERN; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.annotation.Resource; +import java.io.File; +import org.eclipse.xpanse.modules.deployment.deployers.deployertools.DeployerToolUtils; +import org.eclipse.xpanse.modules.deployment.deployers.terraform.terraformlocal.TerraformInstaller; +import org.eclipse.xpanse.modules.models.common.exceptions.InvalidDeployerToolException; +import org.eclipse.xpanse.runtime.cache.redis.AbstractRedisIntegrationTest; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(properties = {"http.request.retry.max.attempts=1", + "enable.redis.distributed.cache=true"}) +class TerraformInstallerTest extends AbstractRedisIntegrationTest { + + @Resource + private TerraformInstaller installer; + @Resource + private DeployerToolUtils deployerToolUtils; + + @Test + void testGetExecutableTerraformByVersion() { + + String requiredVersion = ""; + String terraformPath = installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion); + assertEquals("terraform", terraformPath); + + String requiredVersion1 = "<= v1.7.0"; + String[] operatorAndNumber1 = + deployerToolUtils.getOperatorAndNumberFromRequiredVersion(requiredVersion1); + String terraformPath1 = + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion1); + assertTrue(deployerToolUtils.checkIfExecutorVersionIsValid(new File(terraformPath1), + TERRAFORM_VERSION_OUTPUT_PATTERN, operatorAndNumber1[0], operatorAndNumber1[1])); + + String requiredVersion2 = "= 1.6.0"; + String[] operatorAndNumber2 = deployerToolUtils.getOperatorAndNumberFromRequiredVersion( + requiredVersion2); + String terraformPath2 = + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion2); + assertTrue(deployerToolUtils.checkIfExecutorVersionIsValid(new File(terraformPath2), + TERRAFORM_VERSION_OUTPUT_PATTERN, operatorAndNumber2[0], operatorAndNumber2[1])); + + String requiredVersion3 = ">= v1.8.0"; + String[] operatorAndNumber3 = deployerToolUtils.getOperatorAndNumberFromRequiredVersion( + requiredVersion3); + String terraformPath3 = + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion3); + assertTrue(deployerToolUtils.checkIfExecutorVersionIsValid(new File(terraformPath3), + TERRAFORM_VERSION_OUTPUT_PATTERN, operatorAndNumber3[0], operatorAndNumber3[1])); + + String requiredVersion4 = ">= 100.0.0"; + assertThrows(InvalidDeployerToolException.class, () -> + installer.getExecutorPathThatMatchesRequiredVersion(requiredVersion4)); + + + } +} \ No newline at end of file diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/orcherstrator/PluginManagerTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/orcherstrator/PluginManagerTest.java index 49887432f..e28f6c566 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/orcherstrator/PluginManagerTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/orcherstrator/PluginManagerTest.java @@ -24,8 +24,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ExtendWith(SpringExtension.class) -@SpringBootTest(properties = { - "spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,tofu-maker"}) +@SpringBootTest(properties = {"spring.profiles.active=oauth,zitadel,zitadel-testbed,terraform-boot,tofu-maker,test"}) @AutoConfigureMockMvc class PluginManagerTest { diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/security/RequiredRoleDescriptionCustomizerTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/security/RequiredRoleDescriptionCustomizerTest.java index e27aafa29..621398f15 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/security/RequiredRoleDescriptionCustomizerTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/modules/security/RequiredRoleDescriptionCustomizerTest.java @@ -41,7 +41,7 @@ void testCustomize() throws Exception { new TerraformBootManager(), new TofuMakerManager(), new RedisCacheConfig()); final HandlerMethod handlerMethod = - new HandlerMethod(adminServicesApi, "healthCheck", null); + new HandlerMethod(adminServicesApi, "healthCheck"); final Operation expectedResult = new Operation(); expectedResult.tags(List.of("value")); expectedResult.summary("summary"); diff --git a/runtime/src/test/java/org/eclipse/xpanse/runtime/openapi/OpenApiFileValidationTest.java b/runtime/src/test/java/org/eclipse/xpanse/runtime/openapi/OpenApiFileValidationTest.java index d0aab5856..fae022480 100644 --- a/runtime/src/test/java/org/eclipse/xpanse/runtime/openapi/OpenApiFileValidationTest.java +++ b/runtime/src/test/java/org/eclipse/xpanse/runtime/openapi/OpenApiFileValidationTest.java @@ -23,7 +23,8 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"spring.profiles.active=generate-openapi-doc,noauth"}) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = {"spring.profiles.active=generate-openapi-doc,noauth,test"}) @AutoConfigureMockMvc(print = MockMvcPrint.NONE) public class OpenApiFileValidationTest {