From 94071482b0d8502b2b95fcb6b79925b70dc461f7 Mon Sep 17 00:00:00 2001 From: haodeon <31973188+haodeon@users.noreply.github.com> Date: Fri, 12 Jan 2024 00:29:33 +1300 Subject: [PATCH] feat: Update terraform to 1.6.6 Add otel context for updated runCheckpoint. The code came from OpenTofu. Add tests for updated terraform test command. Add vars param to test() for new test. Add linker flag to exclude -dev prerelease marker. Terraform's minimum go version is now 1.21.5 --- .github/workflows/release.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- README.md | 3 ++- build.py | 3 ++- libterraform.go | 23 +++++++++++++++++++- libterraform/__init__.py | 2 +- libterraform/cli.py | 3 +++ pyproject.toml | 2 +- terraform | 2 +- tests/cli/test_test.py | 29 ++++++++++++++++++++++++-- tests/tf/sleep2/main.tf | 8 +++---- tests/tf/sleep2/valid_sleep.tftest.hcl | 17 +++++++++++++++ 12 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 tests/tf/sleep2/valid_sleep.tftest.hcl diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d49ff5f..c55815d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,9 +16,9 @@ jobs: - name: Check out repository code uses: actions/checkout@v2 - name: Set up GoLang - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: - go-version: '1.18' + go-version: '^1.21.5' - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c4c33a4..7e06b59 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,9 +13,9 @@ jobs: - name: Check out repository code uses: actions/checkout@v2 - name: Set up GoLang - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: - go-version: '1.18' + go-version: '^1.21.5' - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: diff --git a/README.md b/README.md index 7c60a39..86609d0 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ dict_keys(['time_sleep.wait1', 'time_sleep.wait2']) | libterraform | Terraform | |-------------------------------------------------------|-------------------------------------------------------------| +| [0.7.0](https://pypi.org/project/libterraform/0.7.0/) | [1.6.6](https://github.com/hashicorp/terraform/tree/v1.6.6) | | [0.6.0](https://pypi.org/project/libterraform/0.6.0/) | [1.5.7](https://github.com/hashicorp/terraform/tree/v1.5.7) | | [0.5.0](https://pypi.org/project/libterraform/0.5.0/) | [1.3.0](https://github.com/hashicorp/terraform/tree/v1.3.0) | | [0.4.0](https://pypi.org/project/libterraform/0.4.0/) | [1.2.2](https://github.com/hashicorp/terraform/tree/v1.2.2) | @@ -98,7 +99,7 @@ dict_keys(['time_sleep.wait1', 'time_sleep.wait2']) If you want to develop this library, should first prepare the following environments: -- [GoLang](https://go.dev/dl/) (Version 1.18+) +- [GoLang](https://go.dev/dl/) (Version 1.21.5+) - [Python](https://www.python.org/downloads/) (Version 3.7~3.11) - GCC diff --git a/build.py b/build.py index ff06a26..29b6966 100644 --- a/build.py +++ b/build.py @@ -52,7 +52,8 @@ def build(setup_kwargs): try: print(' - Building libterraform') subprocess.check_call( - ['go', 'build', '-buildmode=c-shared', f'-o={lib_filename}', tf_package_name], + ['go', 'build', '-buildmode=c-shared', f'-o={lib_filename}', + "-ldflags", "-X github.com/hashicorp/terraform/version.dev=no", tf_package_name], cwd=terraform_dirname ) shutil.move(lib_path, os.path.join(root, 'libterraform', lib_filename)) diff --git a/libterraform.go b/libterraform.go index da109fa..0519ea9 100644 --- a/libterraform.go +++ b/libterraform.go @@ -1,8 +1,10 @@ package main import ( + "context" "encoding/json" "fmt" + "github.com/apparentlymart/go-shquot/shquot" "github.com/hashicorp/go-plugin" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/disco" @@ -23,6 +25,7 @@ import ( "github.com/hashicorp/terraform/version" "github.com/mitchellh/cli" "github.com/mitchellh/colorstring" + "go.opentelemetry.io/otel/trace" "log" "os" "os/signal" @@ -100,6 +103,24 @@ func RunCli(cArgc C.int, cArgv **C.char, cStdOutFd C.int, cStdErrFd C.int) C.int } }() + err = openTelemetryInit() + if err != nil { + // openTelemetryInit can only fail if Terraform was run with an + // explicit environment variable to enable telemetry collection, + // so in typical use we cannot get here. + Ui.Error(fmt.Sprintf("Could not initialize telemetry: %s", err)) + Ui.Error(fmt.Sprintf("Unset environment variable %s if you don't intend to collect telemetry from Terraform.", openTelemetryExporterEnvVar)) + return 1 + } + var ctx context.Context + var otelSpan trace.Span + { + // At minimum we emit a span covering the entire command execution. + _, displayArgs := shquot.POSIXShellSplit(os.Args) + ctx, otelSpan = tracer.Start(context.Background(), fmt.Sprintf("terraform %s", displayArgs)) + defer otelSpan.End() + } + tmpLogPath := os.Getenv(envTmpLogPath) if tmpLogPath != "" { f, err := os.OpenFile(tmpLogPath, os.O_RDWR|os.O_APPEND, 0666) @@ -262,7 +283,7 @@ func RunCli(cArgc C.int, cArgv **C.char, cStdOutFd C.int, cStdErrFd C.int) C.int commands := NewCommands(meta) // Run checkpoint - go runCheckpoint(config) + go runCheckpoint(ctx, config) // Make sure we clean up any managed plugins at the end of this defer func() { diff --git a/libterraform/__init__.py b/libterraform/__init__.py index ba05930..e5abd80 100644 --- a/libterraform/__init__.py +++ b/libterraform/__init__.py @@ -2,7 +2,7 @@ from ctypes import cdll, c_void_p from libterraform.common import WINDOWS -__version__ = '0.6.0' +__version__ = '0.7.0' root = os.path.dirname(os.path.abspath(__file__)) _lib_filename = 'libterraform.dll' if WINDOWS else 'libterraform.so' diff --git a/libterraform/cli.py b/libterraform/cli.py index 9b3cbd4..7de473a 100644 --- a/libterraform/cli.py +++ b/libterraform/cli.py @@ -1452,6 +1452,7 @@ def untaint( def test( self, check: bool = False, + vars: dict = None, no_color: bool = True, compact_warnings: bool = None, junit_xml: str = None, @@ -1508,6 +1509,7 @@ def test( able to write but that were difficult to model in some way. :param check: Whether to check return code. + :param vars: Set variables in the root module of the configuration. :param no_color: True to output not contain any color. :param compact_warnings: Use a more compact representation for warnings, if this command produces only warnings and no errors. @@ -1519,6 +1521,7 @@ def test( :param options: More command options. """ options.update( + var=vars, no_color=flag(no_color), compact_warnings=flag(compact_warnings), junit_xml=junit_xml, diff --git a/pyproject.toml b/pyproject.toml index 0806e72..9f8fa61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "libterraform" -version = "0.6.0" +version = "0.7.0" description = "Python binding for Terraform." authors = ["Prodesire "] license = "MIT" diff --git a/terraform b/terraform index ee58ac1..eba3240 160000 --- a/terraform +++ b/terraform @@ -1 +1 @@ -Subproject commit ee58ac1851c8a433005df9863ed47796a9f6b5e7 +Subproject commit eba3240dae80b8bdd14d747cc43d056ea22573f4 diff --git a/tests/cli/test_test.py b/tests/cli/test_test.py index 3948d05..4f512ac 100644 --- a/tests/cli/test_test.py +++ b/tests/cli/test_test.py @@ -1,9 +1,34 @@ +import os.path + from libterraform import TerraformCommand +from tests.consts import TF_SLEEP2_DIR class TestTerraformCommandTest: def test_test(self, cli: TerraformCommand): r = cli.test() assert r.retcode == 0, r.error - assert 'The "terraform test" command is experimental' in r.value - assert 'No tests defined' in r.error + assert "Success! 0 passed, 0 failed." in r.value + assert "".__eq__(r.error) + + def test_test_run(self): + cwd = TF_SLEEP2_DIR + tf = os.path.join(cwd, ".terraform") + + cli = TerraformCommand(cwd) + if not os.path.exists(tf): + cli.init() + r = cli.test() + assert r.retcode == 0, r.error + assert "Success! 1 passed, 0 failed." in r.value + + def test_test_assertion_error(self): + cwd = TF_SLEEP2_DIR + tf = os.path.join(cwd, ".terraform") + + cli = TerraformCommand(cwd) + if not os.path.exists(tf): + cli.init() + r = cli.test(vars={"sleep2_time1": "2s"}) + assert r.retcode == 1 + assert "libterraform test success!" in r.error diff --git a/tests/tf/sleep2/main.tf b/tests/tf/sleep2/main.tf index b9a5132..b2d9f9f 100644 --- a/tests/tf/sleep2/main.tf +++ b/tests/tf/sleep2/main.tf @@ -9,17 +9,17 @@ variable "sleep2_time2" { } resource "time_sleep" "sleep2_wait1" { - create_duration = var.time1 + create_duration = var.sleep2_time1 } resource "time_sleep" "sleep2_wait2" { - create_duration = var.time2 + create_duration = var.sleep2_time2 } output "sleep2_wait1_id" { - value = time_sleep.wait1.id + value = time_sleep.sleep2_wait1.id } output "sleep2_wait2_id" { - value = time_sleep.wait2.id + value = time_sleep.sleep2_wait2.id } diff --git a/tests/tf/sleep2/valid_sleep.tftest.hcl b/tests/tf/sleep2/valid_sleep.tftest.hcl new file mode 100644 index 0000000..1404d52 --- /dev/null +++ b/tests/tf/sleep2/valid_sleep.tftest.hcl @@ -0,0 +1,17 @@ +variables { + sleep2_time2 = "2s" +} + +run "valid_sleep_duration" { + + assert { + condition = time_sleep.sleep2_wait1.create_duration == "1s" + error_message = "libterraform test success!" + } + + assert { + condition = time_sleep.sleep2_wait2.create_duration == "2s" + error_message = "Duration did not match expected" + } + +}