From 2adb1dcf84153138ca561a9b5128d0e7a255f320 Mon Sep 17 00:00:00 2001 From: Emre Erkunt Date: Fri, 4 Jun 2021 14:41:06 +0100 Subject: [PATCH] Auto-detection of terraform executable & downloading (#478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated the documentation about new argument * Updated the hashicorp pgp key * Added support for -tf / --terraform-version argument * Updated the CHANGELOG * Fixed the CHANGELOG typo * Updated the changelog * Instead of using -tv / -terraform-version, now it auto-detects * Updated the documentation * Removed old --terraform-version bits from documentation * Removed unused import * Removed unused .travis.yml * Updated the CHANGELOG * Handled the use case where current terraform version is older than the plan file. Co-authored-by: Kaan Katırcıoğlu <32440802+Kudbettin@users.noreply.github.com> --- .travis.yml | 83 ---------- CHANGELOG.md | 8 +- docs/pages/usage/index.md | 19 ++- hashicorp-pgp-key.pub | 146 ++++++++++++++---- .../common/terraform_files.py | 103 +++++++++--- terraform_compliance/main.py | 5 +- .../common/test_terraform_files.py | 26 +++- 7 files changed, 249 insertions(+), 141 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 826acf8a..00000000 --- a/.travis.yml +++ /dev/null @@ -1,83 +0,0 @@ -language: python -python: - - '3.6' -services: - - docker -cache: - pip: true - -env: - global: - - IMAGE_NAME=eerkunt/terraform-compliance -addons: - sonarcloud: - organization: "eerkunt-github" - token: - secure: $SONAR_TOKEN - -install: - - pip install coveralls - - pip install -r requirements.txt - - pip install jinja2-cli tox -after_success: coveralls -jobs: - include: - - stage: Unit Tests - provider: script - script: - - py.test -v - - coverage run --source terraform_compliance setup.py test - - echo "Current tag is $TRAVIS_TAG" - - - stage: Integration Tests - provider: script - script: - - tox -e integration - - - stage: Build & Deploy (PYPI) - script: - - echo "PYPI Build & Deploy" - - echo "Current tag is $TRAVIS_TAG" - - jinja2 -D VERSION=$TRAVIS_TAG terraform_compliance/__init__.py -o terraform_compliance/__init__.py.templated - - cat terraform_compliance/__init__.py.templated - - cp terraform_compliance/__init__.py.templated terraform_compliance/__init__.py - - echo "export RELEASE_VERSION=$(cat /home/travis/build/eerkunt/terraform-compliance/terraform_compliance/__init__.py | grep __version__ | cut -d "'" -f2)" > reqs.sh - - source reqs.sh - - if [[ "$TRAVIS_TAG" != "$RELEASE_VERSION" ]]; then echo "TRAVIS_TAG ($TRAVIS_TAG) does not match with RELEASE_VERSION ($RELEASE_VERSION)"; travis_terminate 1; fi - - python setup.py install_egg_info - if: tag IS present - deploy: - skip_cleanup: true - provider: pypi - user: $PYPI_USER - password: $PYPI_PASS - distributions: "sdist bdist_wheel" - on: - tags: true - fork: false - - - stage: Build & Deploy (Docker) - provider: script - if: tag IS present - script: - - echo "Docker Build & Deploy" - - echo "Validating the version with the tag" - - echo "Current tag is $TRAVIS_TAG" - - jinja2 -D VERSION=$TRAVIS_TAG terraform_compliance/__init__.py -o terraform_compliance/__init__.py.templated - - cat terraform_compliance/__init__.py.templated - - cp terraform_compliance/__init__.py.templated terraform_compliance/__init__.py - - echo "export RELEASE_VERSION=$(cat /home/travis/build/eerkunt/terraform-compliance/terraform_compliance/__init__.py | grep __version__ | cut -d "'" -f2)" > reqs.sh - - source reqs.sh - - if [[ "$TRAVIS_TAG" != "$RELEASE_VERSION" ]]; then echo "TRAVIS_TAG ($TRAVIS_TAG) does not match with RELEASE_VERSION ($RELEASE_VERSION)"; travis_terminate 1; fi - - if [ -z "$RELEASE_VERSION" ]; then echo "Can not identify the version!"; travis_terminate 1; fi - - echo "Getting the latest terraform version from Hashicorp" - - echo "export LATEST_TERRAFORM_VERSION=$(curl https://checkpoint-api.hashicorp.com/v1/check/terraform | jq -r .current_version)" > terraform_version.sh - - source terraform_version.sh - - if [ -z "$LATEST_TERRAFORM_VERSION" ]; then echo "Can not identify latest terraform version!"; travis_terminate 1; fi - - travis_retry docker build --compress --no-cache -t "$IMAGE_NAME" --build-arg VERSION=$RELEASE_VERSION --build-arg LATEST_TERRAFORM_VERSION=$LATEST_TERRAFORM_VERSION --build-arg HASHICORP_PGP_KEY="$(cat hashicorp-pgp-key.pub)" . || travis_terminate 1 - - docker images || travis_terminate 1 - - docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASS" || travis_terminate 1 - - docker tag "$IMAGE_NAME" eerkunt/terraform-compliance:latest || travis_terminate 1 - - docker tag "$IMAGE_NAME" "$IMAGE_NAME":"$RELEASE_VERSION" || travis_terminate 1 - - docker push "$IMAGE_NAME":latest || travis_terminate 1 - - docker push "$IMAGE_NAME":"$RELEASE_VERSION" || travis_terminate 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index b92298eb..dfdf5bbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## Unreleased + +* Added an auto-detection mechanism for terraform version while converting the plan. ([#365](https://github.com/terraform-compliance/cli/issues/365) [#273](https://github.com/terraform-compliance/cli/issues/273) [#381](https://github.com/terraform-compliance/cli/issues/381) [MicrosoftDocs/azure-dev-docs#396](https://github.com/MicrosoftDocs/azure-dev-docs/pull/396) [documentation](https://terraform-compliance.com/pages/usage/#-tv---terraform-version)) +* Updated the hashicorp public key. ([#475](https://github.com/terraform-compliance/cli/issues/475)) +* Fixed a problem where child_modules where not parsed in the plan file. ([#478](https://github.com/terraform-compliance/cli/issues/478)) + ## 1.3.15 (2020-05-12) * Fixed an issue where [exclude resources tag](https://terraform-compliance.com/pages/bdd-references/using_tags.html#exclude-resources) were breaking due to improper `step_obj` creation from `Given` step. ([#468](https://github.com/terraform-compliance/cli/issues/468)) * Disabled ssh host key checking for private repositories ([#474](https://github.com/terraform-compliance/cli/issues/474)) @@ -433,4 +439,4 @@ * Instead of having `untaggable_resources` now `terraform-compliance` auto-detects if a resource has `tag` property. * Removed `terraform-validate` usage. * Introduced internal `terraform-compliance` exceptions instead of generic ones. -* Started to keep a CHANGELOG for better understanding about what happens in the tool :) +* Started to keep a CHANGELOG for better understanding about what happens in the tool :) \ No newline at end of file diff --git a/docs/pages/usage/index.md b/docs/pages/usage/index.md index 0ad52e0c..e6eb968c 100644 --- a/docs/pages/usage/index.md +++ b/docs/pages/usage/index.md @@ -16,7 +16,7 @@ cli parameters. ```shell [~] $ terraform-compliance -h - terraform-compliance v1.0.0 initiated + terraform-compliance v[...] initiated usage: terraform-compliance [-h] --features feature directory --planfile plan_file [--identity [ssh private key]] @@ -36,7 +36,7 @@ cli parameters. SSH Private key that will be use on git authentication. --terraform [terraform_file], -t [terraform_file] - The absolute path to the terraform executable. + The absolute path to the terraform executable. --version, -v show program's version number and exit ``` @@ -136,16 +136,17 @@ Please note that, if the host that you are trying to authenticate already define OPTIONAL {: .label .label-yellow} -In some cases, a `plan` file that is created by a specific version of `terraform` might require the same version -of that `terraform` in order to process that. `terraform-compliance` uses `terraform` in order to parse any plan/state -files that has been created. In these cases you can just provide the same version of `terraform` via ; +`terraform-compliance` will attempt to auto-detect the `terraform` version that is used while creating the plan. It will +attempt to downlod that specific terraform version while converting the plan file into JSON format. + +In case this detection attempt fails, you can also provide a local `terraform` executable ```shell [~] $ terraform-compliance -t /path/to/specific/versin/of/terraform ... ``` -If you are using a [Docker](/pages/installation/docker) version of `terraform-compliance`, `terraform` binary is already -packaged within the Docker Image. In case, you may need to use another version of `terraform` binary, you can still use +If you are using a [Docker](/pages/installation/docker) version of `terraform-compliance`, the latest version of `terraform` binary is already +packaged within the Docker Image. In case the auto-detection failed and you may need to use another version of `terraform` binary, you can still use `-t` to point the local version. `terraform` executable is used to transform `plan.out` file produced from `terraform plan` to `plan.out.json` by running @@ -158,6 +159,8 @@ container by running ; [~] $ terraform show -json plan.out > plan.out.json # To convert the plan.out to JSON format just after the plan ``` + + ### -q / --quit-early {: .d-inline-block } OPTIONAL @@ -214,4 +217,4 @@ This option will let you peek in the stash or run IPython on the current step. In Python shell, context can be accessed via `step.context.stash`. Note that modifications to step object will carry over to the steps below. -It is usually a better idea to inspect the stash *before* the step being debugged. +It is usually a better idea to inspect the stash *before* the step being debugged. \ No newline at end of file diff --git a/hashicorp-pgp-key.pub b/hashicorp-pgp-key.pub index 5364deec..23354c19 100644 --- a/hashicorp-pgp-key.pub +++ b/hashicorp-pgp-key.pub @@ -1,30 +1,122 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 -mQENBFMORM0BCADBRyKO1MhCirazOSVwcfTr1xUxjPvfxD3hjUwHtjsOy/bT6p9f -W2mRPfwnq2JB5As+paL3UGDsSRDnK9KAxQb0NNF4+eVhr/EJ18s3wwXXDMjpIifq -fIm2WyH3G+aRLTLPIpscUNKDyxFOUbsmgXAmJ46Re1fn8uKxKRHbfa39aeuEYWFA -3drdL1WoUngvED7f+RnKBK2G6ZEpO+LDovQk19xGjiMTtPJrjMjZJ3QXqPvx5wca -KSZLr4lMTuoTI/ZXyZy5bD4tShiZz6KcyX27cD70q2iRcEZ0poLKHyEIDAi3TM5k -SwbbWBFd5RNPOR0qzrb/0p9ksKK48IIfH2FvABEBAAG0K0hhc2hpQ29ycCBTZWN1 -cml0eSA8c2VjdXJpdHlAaGFzaGljb3JwLmNvbT6JATgEEwECACIFAlMORM0CGwMG -CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFGFLYc0j/xMyWIIAIPhcVqiQ59n -Jc07gjUX0SWBJAxEG1lKxfzS4Xp+57h2xxTpdotGQ1fZwsihaIqow337YHQI3q0i -SqV534Ms+j/tU7X8sq11xFJIeEVG8PASRCwmryUwghFKPlHETQ8jJ+Y8+1asRydi -psP3B/5Mjhqv/uOK+Vy3zAyIpyDOMtIpOVfjSpCplVRdtSTFWBu9Em7j5I2HMn1w -sJZnJgXKpybpibGiiTtmnFLOwibmprSu04rsnP4ncdC2XRD4wIjoyA+4PKgX3sCO -klEzKryWYBmLkJOMDdo52LttP3279s7XrkLEE7ia0fXa2c12EQ0f0DQ1tGUvyVEW -WmJVccm5bq25AQ0EUw5EzQEIANaPUY04/g7AmYkOMjaCZ6iTp9hB5Rsj/4ee/ln9 -wArzRO9+3eejLWh53FoN1rO+su7tiXJA5YAzVy6tuolrqjM8DBztPxdLBbEi4V+j -2tK0dATdBQBHEh3OJApO2UBtcjaZBT31zrG9K55D+CrcgIVEHAKY8Cb4kLBkb5wM -skn+DrASKU0BNIV1qRsxfiUdQHZfSqtp004nrql1lbFMLFEuiY8FZrkkQ9qduixo -mTT6f34/oiY+Jam3zCK7RDN/OjuWheIPGj/Qbx9JuNiwgX6yRj7OE1tjUx6d8g9y -0H1fmLJbb3WZZbuuGFnK6qrE3bGeY8+AWaJAZ37wpWh1p0cAEQEAAYkBHwQYAQIA -CQUCUw5EzQIbDAAKCRBRhS2HNI/8TJntCAClU7TOO/X053eKF1jqNW4A1qpxctVc -z8eTcY8Om5O4f6a/rfxfNFKn9Qyja/OG1xWNobETy7MiMXYjaa8uUx5iFy6kMVaP -0BXJ59NLZjMARGw6lVTYDTIvzqqqwLxgliSDfSnqUhubGwvykANPO+93BBx89MRG -unNoYGXtPlhNFrAsB1VR8+EyKLv2HQtGCPSFBhrjuzH3gxGibNDDdFQLxxuJWepJ -EK1UbTS4ms0NgZ2Uknqn1WRU1Ki7rE4sTy68iZtWpKQXZEJa0IGnuI2sSINGcXCJ -oEIgXTMyCILo34Fa/C6VCm2WBgz9zZO8/rHIiQm1J5zqz0DrDwKBUM9C -=LYpS +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 +ZF5q4h4I33PSGDdSvGXn9UMY5Isjpg== +=7pIB -----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/terraform_compliance/common/terraform_files.py b/terraform_compliance/common/terraform_files.py index d29e06a4..b348d77f 100644 --- a/terraform_compliance/common/terraform_files.py +++ b/terraform_compliance/common/terraform_files.py @@ -1,6 +1,12 @@ import os +import stat import sys import subprocess +import platform +import urllib.request +import tempfile +from shutil import unpack_archive +import re def which(program): @@ -28,7 +34,7 @@ def convert_terraform_plan_to_json(terraform_plan_file, terraform_executable=Non if terraform_executable is None: terraform_executable = which('terraform') else: - print('Using {} as terraform executable.'.format(terraform_executable)) + print('. Using {} as terraform executable.'.format(terraform_executable)) if terraform_executable is None: sys.stderr.write('ERROR: Could not find "terraform" executable in PATH. Please either use "-t" parameter ' @@ -46,15 +52,19 @@ def convert_terraform_plan_to_json(terraform_plan_file, terraform_executable=Non stdout=FP_plan_file, stderr=subprocess.PIPE) except FileNotFoundError as err: - sys.stderr.write('ERROR: {} does not exist. Please give correct executable for "terraform".\n'.format(terraform_executable)) + sys.stderr.write( + 'ERROR: {} does not exist. Please give correct executable for "terraform".\n'.format(terraform_executable)) sys.stderr.write(' {}\n'.format(str(err))) sys.exit(1) except PermissionError as err: - sys.stderr.write('ERROR: {} is not executable. Please give correct executable for "terraform".\n'.format(terraform_executable)) + sys.stderr.write('ERROR: {} is not executable. Please give correct executable for "terraform".\n'.format( + terraform_executable)) sys.stderr.write(' {}\n'.format(str(err))) sys.exit(1) except OSError as err: - sys.stderr.write('ERROR: {} does not look like terraform. Please give correct executable for "terraform".\n'.format(terraform_executable)) + sys.stderr.write( + 'ERROR: {} does not look like terraform. Please give correct executable for "terraform".\n'.format( + terraform_executable)) sys.stderr.write(' {}\n'.format(str(err))) sys.exit(1) @@ -63,18 +73,73 @@ def convert_terraform_plan_to_json(terraform_plan_file, terraform_executable=Non if terraform.returncode == 0: return '{}.json'.format(terraform_plan_file) - sys.stderr.write('ERROR: Failed to convert terraform plan file to JSON format via terraform. Here is the error :\n') - print(terraform.stdout) - print(terraform.stderr) - - if 'Could not satisfy plugin requirements' in terraform.stderr: - print('Hint: You can avoid this problem by converting your plan file to a JSON file via running;\n ' - '\n # terraform show -json {} > {}.json' - '\n\n OR' - '\n # terraform init' - '\n\n in {} directory and then pass (with -p) {}.json to terraform-compliance'.format(terraform_plan_file, - terraform_plan_file, - path, - terraform_plan_file)) - - sys.exit(1) + # Ok here we are going to fail because plan file has been created by a different terraform + # So, we will attempt a detection of terraform version here. Hoping that the terraform stderr + # about this error will not change in the future. + target_version, current_version = detect_required_terraform_version(terraform.stderr) + + if current_version is not None and target_version is not None: + sys.stderr.write('. Plan file has been created by terraform v{}, ' + 'but you have v{}\n'.format(target_version, current_version)) + terraform_executable = download_terraform(target_version) + return convert_terraform_plan_to_json(terraform_plan_file=terraform_plan_file, + terraform_executable=terraform_executable) + + + else: + sys.stderr.write('ERROR: Failed to convert terraform plan file to JSON format via terraform. Here is the error :\n') + print(terraform.stdout) + print(terraform.stderr) + + if 'Could not satisfy plugin requirements' in terraform.stderr: + print('Hint: You can avoid this problem by converting your plan file to a JSON file via running;\n ' + '\n # terraform show -json {} > {}.json' + '\n\n OR' + '\n # terraform init' + '\n\n in {} directory and then pass (with -p) {}.json to terraform-compliance'.format(terraform_plan_file, + terraform_plan_file, + path, + terraform_plan_file)) + + sys.exit(1) + + +def get_platform_details(): + system = platform.system().lower() + arch = "amd64" if platform.machine() == "x86_64" else "386" + return system, arch + + +def download_terraform(version): + system, arch = get_platform_details() + tmp_dir = tempfile.gettempdir() + terraform_file = "{}/terraform_{}_{}_{}".format(tmp_dir, version, system, arch) + + if os.path.isfile(terraform_file): + print('. Using cached {}'.format(terraform_file)) + return terraform_file + + url = 'https://releases.hashicorp.com/terraform/{}/terraform_{}_{}_{}.zip'.format(version, version, system, + arch) + print('. Downloading terraform v{} from {} ...'.format(version, url)) + urllib.request.urlretrieve(url, '{}.zip'.format(terraform_file)) + print('. Unpacking {}.zip'.format(terraform_file)) + unpack_archive('{}.zip'.format(terraform_file), tmp_dir) + os.rename('{}/terraform'.format(tmp_dir), terraform_file) + st = os.stat(terraform_file) + os.chmod(terraform_file, st.st_mode | stat.S_IEXEC) + return terraform_file + + +def detect_required_terraform_version(string): + match_regex = [ + r'([0-9.]+), but this is ([0-9.]+); plan files cannot be transferred between', + r'by Terraform v([0-9.]+), which is newer than current v([0-9.]+);' + ] + for pattern in match_regex: + match = re.search(pattern, string, re.DOTALL) + + if match is not None: + return match.group(1), match.group(2) + + return None, None \ No newline at end of file diff --git a/terraform_compliance/main.py b/terraform_compliance/main.py index c3eef23d..38e90d72 100644 --- a/terraform_compliance/main.py +++ b/terraform_compliance/main.py @@ -51,7 +51,8 @@ def cli(arghandling=ArgHandling(), argparser=ArgumentParser(prog=__app_name__, help='Do not output any scenarios, just write results or failures', required=False) parser.add_argument('--identity', '-i', dest='ssh_key', metavar='ssh private key', type=str, nargs='?', help='SSH Private key that will be use on git authentication.', required=False) - parser.add_argument('--debug', '-d', dest='debug', action='store_true', help='Turns on debugging mode', required=False) + parser.add_argument('--debug', '-d', dest='debug', action='store_true', help='Turns on debugging mode', + required=False) parser.add_argument('--version', '-v', action='version', version=__version__) @@ -150,4 +151,4 @@ def cli(arghandling=ArgHandling(), argparser=ArgumentParser(prog=__app_name__, if __name__ == '__main__': - cli() + cli() \ No newline at end of file diff --git a/tests/terraform_compliance/common/test_terraform_files.py b/tests/terraform_compliance/common/test_terraform_files.py index f49730d1..8cf55415 100644 --- a/tests/terraform_compliance/common/test_terraform_files.py +++ b/tests/terraform_compliance/common/test_terraform_files.py @@ -1,5 +1,11 @@ from unittest import TestCase -from terraform_compliance.common.terraform_files import which, convert_terraform_plan_to_json +from terraform_compliance.common.terraform_files import ( + which, + convert_terraform_plan_to_json, + download_terraform, + get_platform_details, + detect_required_terraform_version +) import os from mock import patch @@ -17,3 +23,21 @@ def test_which_failure(self): def test_convert_terraform_plan_to_json(self, *args): with self.assertRaises(SystemExit): convert_terraform_plan_to_json('some_file') + + def test_get_platform_details(self): + system, arch = get_platform_details() + print("System: {}\tArch: {}".format(system, arch)) + self.assertIsNot(system, "") + self.assertIsNot(arch, "") + + # def test_downloading_terraform(self, *args): + # tf = download_terraform('0.15.3') + + def test_detect_required_terraform_version(self): + current, target = detect_required_terraform_version('0.14.0, but this is 0.15.1; plan files cannot be transferred between') + self.assertEqual(current, '0.14.0') + self.assertEqual(target, '0.15.1') + + current, target = detect_required_terraform_version('') + self.assertIsNone(current) + self.assertIsNone(target) \ No newline at end of file