Skip to content

Commit

Permalink
support multiple versions of openTofu (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
baixinsui authored Oct 9, 2024
1 parent aeea5bf commit b2476cf
Show file tree
Hide file tree
Showing 28 changed files with 1,084 additions and 74 deletions.
14 changes: 13 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ on:
- Major Version
- Minor Version
- Patch Version

DEFAULT_OPENTOFU_VERSION:
type: string
description: Input the default version of OpenTofu to install
required: true
default: '1.6.0'
OPENTOFU_VERSIONS:
type: string
description: Input the multiple versions of OpenTofu to install
required: true
default: '1.6.0,1.7.0,1.8.0'
env:
BOT_USER_NAME: eclipse-xpanse-bot
BOT_EMAIL_ID: [email protected]
Expand Down Expand Up @@ -103,6 +112,9 @@ jobs:
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.new_version.outputs.next-version }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false
build-args: |
DEFAULT_OPENTOFU_VERSION=${{ github.event.inputs.DEFAULT_OPENTOFU_VERSION }}
OPENTOFU_VERSIONS=${{ github.event.inputs.OPENTOFU_VERSIONS }}
- name: Push POM updates with release version
uses: EndBug/add-and-commit@v9
Expand Down
15 changes: 10 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S tofu-maker && adduser -S -G tofu-maker tofu-maker
RUN apk update && \
apk add --no-cache unzip wget
ENV OPENTOFU_VERSION=1.6.0
RUN wget https://github.com/opentofu/opentofu/releases/download/v${OPENTOFU_VERSION}/tofu_${OPENTOFU_VERSION}_linux_amd64.zip
RUN unzip tofu_${OPENTOFU_VERSION}_linux_amd64.zip
RUN mv tofu /usr/bin/tofu

ENV OPENTOFU_INSTALL_PATH=/opt/opentofu
ENV DEFAULT_OPENTOFU_VERSION=1.6.0
ENV OPENTOFU_VERSIONS=1.6.0,1.7.0,1.8.0
COPY install_opentofu.sh /install_opentofu.sh
RUN chmod +x /install_opentofu.sh
RUN echo "Downloading and installing OpenTofu with multiple versions $OPENTOFU_VERSIONS into path $OPENTOFU_INSTALL_PATH"; \
/install_opentofu.sh "$OPENTOFU_INSTALL_PATH" "$DEFAULT_OPENTOFU_VERSION" "$OPENTOFU_VERSIONS"

COPY target/tofu-maker-*.jar tofu-maker.jar
USER tofu-maker
ENTRYPOINT ["java","-jar","tofu-maker.jar"]
ENTRYPOINT ["java", "-Dopentofu.install.dir=${OPENTOFU_INSTALL_PATH}", "-Dopentofu.version=${OPENTOFU_VERSIONS}", "-jar", "tofu-maker.jar"]
43 changes: 43 additions & 0 deletions install_opentofu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/sh
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Error:OPENTOFU_INSTALL_PATH and DEFAULT_OPENTOFU_VERSION could not be empty."
exit 1
fi
OPENTOFU_INSTALL_PATH="$1"
DEFAULT_OPENTOFU_VERSION="$2"
OPENTOFU_VERSIONS="$3"

mkdir -p "${OPENTOFU_INSTALL_PATH}"
# install default version of OpenTofu into system path and custom path
echo "Start installing OpenTofu with default version ${DEFAULT_OPENTOFU_VERSION}";
wget -c "https://github.com/opentofu/opentofu/releases/download/v${DEFAULT_OPENTOFU_VERSION}/tofu_${DEFAULT_OPENTOFU_VERSION}_linux_amd64.zip";
if [ -f "tofu_${DEFAULT_OPENTOFU_VERSION}_linux_amd64.zip" ]; then
unzip -o "tofu_${DEFAULT_OPENTOFU_VERSION}_linux_amd64.zip";
cp -f tofu /usr/bin/tofu;
chmod +x /usr/bin/tofu;
mv -f tofu "${OPENTOFU_INSTALL_PATH}/tofu-${DEFAULT_OPENTOFU_VERSION}";
chmod +x "${OPENTOFU_INSTALL_PATH}/tofu-${DEFAULT_OPENTOFU_VERSION}";
rm "tofu_${DEFAULT_OPENTOFU_VERSION}_linux_amd64.zip";
echo "Installed OpenTofu with default version ${DEFAULT_OPENTOFU_VERSION} into path ${OPENTOFU_INSTALL_PATH} successfully."
else
echo "Failed to download zip package of OpenTofu with default version tofu_${DEFAULT_OPENTOFU_VERSION}_linux_amd64.zip"
fi
if [ -z "$OPENTOFU_VERSIONS" ]; then
echo "No OpenTofu versions specified, skip installing OpenTofu versions."
exit 0
fi
# Install versions of OpenTofu specified in OPENTOFU_VERSIONS into custom path
VERSIONS=$(echo "$OPENTOFU_VERSIONS" | tr ',' '\n' | tr -d ' ')
for version in $VERSIONS; do
echo "Start installing OpenTofu with version ${version} into path ${OPENTOFU_INSTALL_PATH}";
wget -c "https://github.com/opentofu/opentofu/releases/download/v${version}/tofu_${version}_linux_amd64.zip";
if [ ! -f "tofu_${version}_linux_amd64.zip" ]; then
echo "Failed to download zip package of OpenTofu with version tofu_${version}_linux_amd64.zip"
continue
fi
unzip -o "tofu_${version}_linux_amd64.zip";
mv -f tofu "${OPENTOFU_INSTALL_PATH}/tofu-${version}";
chmod +x "${OPENTOFU_INSTALL_PATH}/tofu-${version}";
rm -rf "tofu_${version}_linux_amd64.zip";
echo "Installed OpenTofu with version ${version} into path ${OPENTOFU_INSTALL_PATH} successfully."
done
21 changes: 20 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
<opentelemetry.version>1.32.0</opentelemetry.version>
<eclipse.dash.tool.plugin>1.1.0</eclipse.dash.tool.plugin>
<jgit.version>7.0.0.202409031743-r</jgit.version>
<semver4j.version>5.4.0</semver4j.version>
<github-api.version>1.326</github-api.version>
<maven.complier.plugin.version>3.13.0</maven.complier.plugin.version>
<maven.surefire.plugin.version>3.5.0</maven.surefire.plugin.version>
<maven.enforcer.plugin.version>3.5.0</maven.enforcer.plugin.version>
Expand All @@ -52,7 +54,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down Expand Up @@ -111,6 +120,16 @@
<artifactId>spring-retry</artifactId>
<version>${spring.retry.version}</version>
</dependency>
<dependency>
<groupId>org.semver4j</groupId>
<artifactId>semver4j</artifactId>
<version>${semver4j.version}</version>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>${github-api.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableAsync;

Expand All @@ -16,6 +17,7 @@
*/
@EnableRetry
@EnableAsync
@EnableCaching
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class OpenTofuMakerApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import java.util.Objects;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -25,6 +27,7 @@
import org.eclipse.xpanse.tofu.maker.models.response.OpenTofuResult;
import org.eclipse.xpanse.tofu.maker.models.validation.OpenTofuValidationResult;
import org.eclipse.xpanse.tofu.maker.opentofu.service.OpenTofuDirectoryService;
import org.eclipse.xpanse.tofu.maker.opentofu.tool.OpenTofuVersionsHelper;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
Expand Down Expand Up @@ -66,15 +69,20 @@ public OpenTofuMakerFromDirectoryApi(
@Tag(name = "OpenTofuFromDirectory", description =
"APIs for running OpenTofu commands inside a provided directory.")
@Operation(description = "Validate the OpenTofu modules in the given directory.")
@GetMapping(value = "/validate/{module_directory}", produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping(value = "/validate/{module_directory}/{opentofu_version}", produces =
MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public OpenTofuValidationResult validateFromDirectory(
@Parameter(name = "module_directory",
description = "directory name where the OpenTofu module files exist.")
@PathVariable("module_directory") String moduleDirectory) {
@PathVariable("module_directory") String moduleDirectory,
@Parameter(name = "opentofu_version",
description = "version of OpenTofu to execute the module files.")
@NotBlank @Pattern(regexp = OpenTofuVersionsHelper.OPENTOFU_REQUIRED_VERSION_REGEX)
@PathVariable("opentofu_version") String openTofuVersion) {
UUID uuid = UUID.randomUUID();
MDC.put(REQUEST_ID, uuid.toString());
return openTofuDirectoryService.tfValidateFromDirectory(moduleDirectory);
return openTofuDirectoryService.tfValidateFromDirectory(moduleDirectory, openTofuVersion);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Huawei Inc.
*
*/

package org.eclipse.xpanse.tofu.maker.cache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Caffeine cache configuration class.
*/
@Slf4j
@Configuration
public class CaffeineCacheConfig {
public static final String OPENTOFU_VERSIONS_CACHE_NAME = "OPENTOFU_VERSIONS_CACHE";

/**
* Config cache manager with caffeine.
*
* @return caffeineCacheManager
*/
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.registerCustomCache(OPENTOFU_VERSIONS_CACHE_NAME,
getOpenTofuVersionsCache());
return cacheManager;
}


private Cache<Object, Object> getOpenTofuVersionsCache() {
return Caffeine.newBuilder().build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Huawei Inc.
*/

package org.eclipse.xpanse.tofu.maker.models.exceptions;

/**
* Defines possible exceptions returned by OpenTofu version invalid.
*/
public class InvalidOpenTofuToolException extends RuntimeException {

public InvalidOpenTofuToolException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,16 @@ public Response handleGitRepoCloneException(GitRepoCloneException ex) {
return Response.errorResponse(ResultType.INVALID_GIT_REPO_DETAILS,
Collections.singletonList(failMessage));
}

/**
* Exception handler for InvalidOpenTofuToolException.
*/
@ExceptionHandler({InvalidOpenTofuToolException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Response handleInvalidOpenTofuToolException(
InvalidOpenTofuToolException ex) {
return Response.errorResponse(ResultType.INVALID_OPENTOFU_TOOL,
Collections.singletonList(ex.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ public class OpenTofuPlan {
@NotNull
@Schema(description = "OpenTofu plan as a JSON string")
String plan;

@Schema(description = "The exact version of the OpenTofu which executed the scripts.")
String openTofuVersion;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
package org.eclipse.xpanse.tofu.maker.models.plan;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import lombok.Data;
import org.eclipse.xpanse.tofu.maker.opentofu.tool.OpenTofuVersionsHelper;

/**
* Data model for the generating open tofu plan.
Expand All @@ -21,6 +24,12 @@ public class OpenTofuPlanFromDirectoryRequest {
@Schema(description = "Id of the request.")
UUID requestId;

@NotNull
@NotBlank
@Pattern(regexp = OpenTofuVersionsHelper.OPENTOFU_REQUIRED_VERSION_REGEX)
@Schema(description = "The required version of the OpenTofu which will execute the scripts.")
String openTofuVersion;

@NotNull
@Schema(description = "Key-value pairs of variables that must be used to execute the "
+ "OpenTofu request.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
package org.eclipse.xpanse.tofu.maker.models.request.directory;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import lombok.Data;
import org.eclipse.xpanse.tofu.maker.opentofu.tool.OpenTofuVersionsHelper;

/**
* Data model for the OpenTofu deploy requests.
Expand All @@ -21,6 +24,12 @@ public class OpenTofuDeployFromDirectoryRequest {
@Schema(description = "Id of the request.")
UUID requestId;

@NotNull
@NotBlank
@Pattern(regexp = OpenTofuVersionsHelper.OPENTOFU_REQUIRED_VERSION_REGEX)
@Schema(description = "The required version of the OpenTofu which will execute the scripts.")
String openTofuVersion;

@NotNull
@Schema(description = "Flag to control if the deployment must only generate the OpenTofu "
+ "or it must also apply the changes.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import static io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.TRUE;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import lombok.Data;
import org.eclipse.xpanse.tofu.maker.opentofu.tool.OpenTofuVersionsHelper;

/**
* Data model for the OpenTofu destroy requests.
Expand All @@ -23,6 +26,12 @@ public class OpenTofuDestroyFromDirectoryRequest {
@Schema(description = "Id of the request.")
UUID requestId;

@NotNull
@NotBlank
@Pattern(regexp = OpenTofuVersionsHelper.OPENTOFU_REQUIRED_VERSION_REGEX)
@Schema(description = "The required version of the OpenTofu which will execute the scripts.")
String openTofuVersion;

@NotNull
@Schema(description = "Key-value pairs of regular variables that must be used to execute the "
+ "OpenTofu request.", additionalProperties = TRUE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import static io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.TRUE;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import lombok.Data;
import org.eclipse.xpanse.tofu.maker.opentofu.tool.OpenTofuVersionsHelper;

/**
* Data model for the OpenTofu modify requests.
Expand All @@ -23,6 +26,12 @@ public class OpenTofuModifyFromDirectoryRequest {
@Schema(description = "Id of the request.")
UUID requestId;

@NotNull
@NotBlank
@Pattern(regexp = OpenTofuVersionsHelper.OPENTOFU_REQUIRED_VERSION_REGEX)
@Schema(description = "The required version of the OpenTofu which will execute the scripts.")
String openTofuVersion;

@NotNull
@Schema(description = "Flag to control if the deployment must only generate the OpenTofu "
+ "or it must also apply the changes.")
Expand Down
Loading

0 comments on commit b2476cf

Please sign in to comment.