diff --git a/README.md b/README.md index 4a7693f..1824ee0 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,18 @@ public class SpringStarterSampleApp implements CommandLineRunner { } ``` +### OpenTofu Support + +When using with opentofu you need to use the terraformProcessData like the following: + +```java + TerraformProcessData terraformProcessData = TerraformProcessData.builder() + .terraformVersion("1.6.0") + .workingDirectory(new File("/some/terraform/path")) + .tofu(true) + .build(); +``` + ### Custom Terraform Releases URL You can customize the URL from where you download your terraform binary. diff --git a/pom.xml b/pom.xml index 432476c..69dd9df 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 - 0.11.2 + 0.12.0 azbuilder https://sonarcloud.io true diff --git a/terraform-client/pom.xml b/terraform-client/pom.xml index 5a7a2ad..bcdb1ea 100644 --- a/terraform-client/pom.xml +++ b/terraform-client/pom.xml @@ -23,7 +23,7 @@ 3.3.0 3.6.2 3.1.0 - 0.11.2 + 0.12.0 false 2.15.0 3.9.5 diff --git a/terraform-client/src/main/java/org/terrakube/terraform/TerraformClient.java b/terraform-client/src/main/java/org/terrakube/terraform/TerraformClient.java index 27825db..b7ec1fe 100644 --- a/terraform-client/src/main/java/org/terrakube/terraform/TerraformClient.java +++ b/terraform-client/src/main/java/org/terrakube/terraform/TerraformClient.java @@ -253,7 +253,7 @@ private ProcessLauncher getTerraformLauncher(TerraformCommand command) throws IO private ProcessLauncher getTerraformLauncher(TerraformProcessData terraformProcessData, Consumer outputListener, Consumer errorListener, TerraformCommand command) throws IOException { TerraformDownloader terraformDownloader = createTerraformDownloader(); - String terraformPath = terraformDownloader.downloadTerraformVersion(terraformProcessData.getTerraformVersion()); + String terraformPath = terraformProcessData.isTofu() ? terraformDownloader.downloadTofuVersion(terraformProcessData.getTerraformVersion()) : terraformDownloader.downloadTerraformVersion(terraformProcessData.getTerraformVersion()); if (terraformProcessData.sshFile != null && command.equals(TerraformCommand.init)) { return getTerraformInitWithSSH(terraformPath, terraformProcessData, outputListener, errorListener); diff --git a/terraform-client/src/main/java/org/terrakube/terraform/TerraformDownloader.java b/terraform-client/src/main/java/org/terrakube/terraform/TerraformDownloader.java index fb2cb89..69aae46 100644 --- a/terraform-client/src/main/java/org/terrakube/terraform/TerraformDownloader.java +++ b/terraform-client/src/main/java/org/terrakube/terraform/TerraformDownloader.java @@ -23,20 +23,25 @@ public class TerraformDownloader { private static final String TERRAFORM_DOWNLOAD_DIRECTORY = "/.terraform-spring-boot/download/"; + private static final String TOFU_DOWNLOAD_DIRECTORY = "/.terraform-spring-boot/download/tofu/"; private static final String TERRAFORM_DIRECTORY = "/.terraform-spring-boot/terraform/"; + private static final String TOFU_DIRECTORY = "/.terraform-spring-boot/tofu/"; private static final String TEMP_DIRECTORY = "/.terraform-spring-boot/"; public static final String TERRAFORM_RELEASES_URL = "https://releases.hashicorp.com/terraform/index.json"; private TerraformResponse terraformReleases; private File terraformDownloadDirectory; + + private File tofuDownloadDirectory; private File terraformDirectory; private String userHomeDirectory; public TerraformDownloader() { try { - log.info("Initialize TerraformDownloader using default URL"); + log.info("Initialize Terraform and Tofu Downloader using default URL"); createDownloadTempDirectory(); + createDownloadTofuTempDirectory(); getTerraformReleases(TERRAFORM_RELEASES_URL); } catch (IOException ex) { log.error(ex.getMessage()); @@ -48,6 +53,7 @@ public TerraformDownloader(String terraformReleasesUrl) { try { createDownloadTempDirectory(); + createDownloadTofuTempDirectory(); getTerraformReleases(terraformReleasesUrl); } catch (IOException ex) { log.error(ex.getMessage()); @@ -75,6 +81,27 @@ private void createDownloadTempDirectory() throws IOException { log.info("Validate/Create terraform directory: {}", terrafomVersionPath); } + private void createDownloadTofuTempDirectory() throws IOException { + this.userHomeDirectory = FileUtils.getUserDirectoryPath(); + log.info("User Home Directory for tofu download: {}", this.userHomeDirectory); + + String tofuDownloadPath = userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DOWNLOAD_DIRECTORY + )); + this.tofuDownloadDirectory = new File(tofuDownloadPath); + FileUtils.forceMkdir(this.tofuDownloadDirectory); + log.info("Validate/Create tofu download temp directory: {}", tofuDownloadPath); + + String tofuVersionPath = userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DIRECTORY + )); + this.terraformDirectory = new File(tofuVersionPath); + FileUtils.forceMkdir(this.terraformDirectory); + log.info("Validate/Create tofu directory: {}", tofuVersionPath); + } + private void getTerraformReleases(String terraformReleasesUrl) throws IOException { log.info("Downloading terraform releases list"); @@ -168,8 +195,69 @@ public String downloadTerraformVersion(String terraformVersion) throws IOExcepti return terraformFilePath; } + + public String downloadTofuVersion(String tofuVersion) throws IOException { + log.info("Downloading tofu version {} architecture {} Type {}", tofuVersion, SystemUtils.OS_ARCH, SystemUtils.OS_NAME); + // "https://github.com/opentofu/opentofu/releases/download/v1.6.0/tofu_1.6.0_windows_386.zip" + // "https://github.com/opentofu/opentofu/releases/download/v1.6.0/tofu_1.6.0_linux_amd64.zip" + // "https://github.com/opentofu/opentofu/releases/download/v1.6.0/tofu_1.6.0_darwin_amd64.zip" + String defaultDownloadUrl = "https://github.com/opentofu/opentofu/releases/download/v%s/tofu_%s_%s_%s.zip"; + String defaultFileName = "tofu_%s_%s_%s.zip"; + + String tofuZipReleaseURL = String.format(defaultDownloadUrl, tofuVersion, tofuVersion, getOs(), SystemUtils.OS_ARCH ); + String tofuFileName = String.format(defaultFileName, tofuVersion, getOs(), SystemUtils.OS_ARCH ); + String tofuFilePath = ""; + if (tofuVersion != null) { + String fileName = tofuFileName; + + if (!FileUtils.directoryContains(this.tofuDownloadDirectory, new File( + this.userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DOWNLOAD_DIRECTORY.concat("/").concat(fileName) + ))))) { + + log.info("Downloading Tofu from: {}", tofuZipReleaseURL); + try { + File tofuZipFile = new File( + this.userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DOWNLOAD_DIRECTORY.concat(fileName) + ))); + + FileUtils.copyURLToFile(new URL(tofuZipReleaseURL), tofuZipFile); + + tofuFilePath = unzipTofuVersion(tofuVersion, tofuZipFile); + } catch (IOException exception) { + throw new IOException("Unable to download tofu ".concat(tofuZipReleaseURL)); + } + } else { + log.info("{} tofu already exists", fileName); + + return this.userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DIRECTORY.concat(tofuVersion.concat("/").concat("tofu")) + ) + ); + } + } else { + throw new IllegalArgumentException("Invalid Tofu Version"); + } + + return tofuFilePath; + } + + public String getOs(){ + if (SystemUtils.IS_OS_LINUX) + return "linux"; + if (SystemUtils.IS_OS_MAC) + return "darwin"; + if (SystemUtils.IS_OS_WINDOWS) + return "windows"; + return "linux"; + } + private String unzipTerraformVersion(String terraformVersion, File terraformZipFile) throws IOException { - createTerraformVersionDirectory(terraformVersion); + createVersionDirectory(terraformVersion, TERRAFORM_DIRECTORY); String newFilePath = null; try (ZipInputStream zis = new ZipInputStream(new FileInputStream(terraformZipFile))) { ZipEntry zipEntry = zis.getNextEntry(); @@ -215,11 +303,61 @@ private String unzipTerraformVersion(String terraformVersion, File terraformZipF return newFilePath; } - private void createTerraformVersionDirectory(String terraformVersion) throws IOException { + private String unzipTofuVersion(String tofuVersion, File tofuZipFile) throws IOException { + createVersionDirectory(tofuVersion, TOFU_DIRECTORY); + String newFilePath = null; + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(tofuZipFile))) { + ZipEntry zipEntry = zis.getNextEntry(); + + byte[] buffer = new byte[1024]; + while (zipEntry != null) { + newFilePath = this.userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DIRECTORY.concat(tofuVersion.concat("/").concat(zipEntry.getName())) + ) + ); + log.info("Unzip Tofu files: {}", newFilePath); + File newTofuFile = new File(newFilePath); + if (zipEntry.isDirectory()) { + if (!newTofuFile.isDirectory() && !newTofuFile.mkdirs()) { + throw new IOException("Failed to create directory for" + newTofuFile); + } + } else { + File parent = newTofuFile.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IOException("Failed to create directory for" + parent); + } + + try (FileOutputStream file = new FileOutputStream(newTofuFile)) { + int len; + while ((len = zis.read(buffer)) > 0) { + file.write(buffer, 0, len); + } + } + + if (SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC) { + File updateAccess = new File(newFilePath); + if (updateAccess.setExecutable(true, true)) + log.info("Tofu setExecutable successful"); + else + log.error("Tofu setExecutable successful"); + } + } + zipEntry = zis.getNextEntry(); + } + zis.closeEntry(); + } + return this.userHomeDirectory.concat( + FilenameUtils.separatorsToSystem( + TOFU_DIRECTORY.concat(tofuVersion.concat("/").concat("tofu")) + )); + } + + private void createVersionDirectory(String version, String directoryPath) throws IOException { File terraformVersionDirectory = new File( userHomeDirectory.concat( FilenameUtils.separatorsToSystem( - TERRAFORM_DIRECTORY.concat(terraformVersion) + directoryPath.concat(version) ))); FileUtils.forceMkdir(terraformVersionDirectory); } @@ -253,4 +391,4 @@ class TerraformVersion { private String shasums_signature; private List shasums_signatures; private List builds; -} \ No newline at end of file +} diff --git a/terraform-client/src/main/java/org/terrakube/terraform/TerraformProcessData.java b/terraform-client/src/main/java/org/terrakube/terraform/TerraformProcessData.java index 9b17ea0..3e0b093 100644 --- a/terraform-client/src/main/java/org/terrakube/terraform/TerraformProcessData.java +++ b/terraform-client/src/main/java/org/terrakube/terraform/TerraformProcessData.java @@ -19,6 +19,8 @@ public class TerraformProcessData { boolean refresh = true; @Builder.Default boolean refreshOnly = false; + @Builder.Default + boolean tofu = false; @Singular Map terraformVariables; @Singular Map terraformEnvironmentVariables; } diff --git a/terraform-spring-boot-autoconfigure/pom.xml b/terraform-spring-boot-autoconfigure/pom.xml index 3206df7..cf33b34 100644 --- a/terraform-spring-boot-autoconfigure/pom.xml +++ b/terraform-spring-boot-autoconfigure/pom.xml @@ -22,7 +22,7 @@ 3.3.0 3.6.2 3.1.0 - 0.11.2 + 0.12.0 false 1.18.30 11 diff --git a/terraform-spring-boot-samples/pom.xml b/terraform-spring-boot-samples/pom.xml index 0eec0eb..a795598 100644 --- a/terraform-spring-boot-samples/pom.xml +++ b/terraform-spring-boot-samples/pom.xml @@ -18,7 +18,7 @@ UTF-8 - 0.11.2 + 0.12.0 true 11 11 diff --git a/terraform-spring-boot-samples/raw-client-library-sample/pom.xml b/terraform-spring-boot-samples/raw-client-library-sample/pom.xml index 5197025..a5c0b0b 100644 --- a/terraform-spring-boot-samples/raw-client-library-sample/pom.xml +++ b/terraform-spring-boot-samples/raw-client-library-sample/pom.xml @@ -20,7 +20,7 @@ UTF-8 true - 0.11.2 + 0.12.0 11 11 diff --git a/terraform-spring-boot-samples/spring-starter-sample/pom.xml b/terraform-spring-boot-samples/spring-starter-sample/pom.xml index 0567622..613ee4a 100644 --- a/terraform-spring-boot-samples/spring-starter-sample/pom.xml +++ b/terraform-spring-boot-samples/spring-starter-sample/pom.xml @@ -20,7 +20,7 @@ UTF-8 true - 0.11.2 + 0.12.0 11 11 diff --git a/terraform-spring-boot-starter/pom.xml b/terraform-spring-boot-starter/pom.xml index 12300b9..0e34f4b 100644 --- a/terraform-spring-boot-starter/pom.xml +++ b/terraform-spring-boot-starter/pom.xml @@ -17,7 +17,7 @@ 3.3.0 3.6.2 3.1.0 - 0.11.2 + 0.12.0 false 11 11