Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Terraform] feature fallback fix #912

Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2f67147
implementation of fallback logic for cosign, terraform
gauravsaini04 Mar 13, 2024
d7bd148
tflint, terragrunt - wrote fallback logic for these
gauravsaini04 Mar 13, 2024
39ab5bb
misc change
gauravsaini04 Mar 13, 2024
1975cc3
Merge branch 'devcontainers:main' into terraform_feature_fallback_fix
gauravsaini04 Mar 13, 2024
a2c8da6
[Terraform] - fallback - apply
gauravsaini04 Mar 14, 2024
73af073
Merge branch 'devcontainers:main' into terraform_feature_fallback_fix
gauravsaini04 Mar 17, 2024
e8b8be3
changes as requested by review comments
gauravsaini04 Mar 17, 2024
b8cbd52
misc change
gauravsaini04 Mar 17, 2024
4860462
misc change
gauravsaini04 Mar 17, 2024
e8d5c74
Merge branch 'devcontainers:main' into terraform_feature_fallback_fix
gauravsaini04 Mar 26, 2024
c27c654
Merge branch 'devcontainers:main' into terraform_feature_fallback_fix
gauravsaini04 Mar 27, 2024
9a93a40
2-step fallback implemented in install.sh, test cases yet to be updated
gauravsaini04 Mar 27, 2024
3e64925
install jq for ubuntu:focal, jammy & debian:11, 12
gauravsaini04 Mar 27, 2024
8eb282a
test for terraform_docs updated
gauravsaini04 Mar 27, 2024
2dbf47e
tfsec test updated
gauravsaini04 Mar 27, 2024
c2715ab
terraform fallback test updated
gauravsaini04 Mar 27, 2024
d639c4d
misc change
gauravsaini04 Mar 27, 2024
2f701d6
added tflint fallback test file
gauravsaini04 Mar 27, 2024
4a983e6
terragrunt fallback test file added
gauravsaini04 Mar 27, 2024
cda53a1
minor change in tfsec test file
gauravsaini04 Mar 27, 2024
2814ddf
few more changes to cosign, terragrunt installations..
gauravsaini04 Mar 27, 2024
8154006
changes to test files for terragrunt & cosign
gauravsaini04 Mar 27, 2024
541a89f
Merge branch 'devcontainers:main' into terraform_feature_fallback_fix
gauravsaini04 Mar 27, 2024
9c7be43
Changes acc. to review comments for pr
gauravsaini04 Mar 27, 2024
0dc47b8
changes requested
gauravsaini04 Mar 27, 2024
0f3bfaf
changes as requested in review comments !
gauravsaini04 Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/terraform/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "terraform",
"version": "1.3.5",
"version": "1.3.6",
"name": "Terraform, tflint, and TFGrunt",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/terraform",
"description": "Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects latest version and installs needed dependencies.",
Expand Down
202 changes: 185 additions & 17 deletions src/terraform/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,47 @@ find_version_from_git_tags() {
echo "${variable_name}=${!variable_name}"
}

# Use semver logic to decrement a version number then look for the closest match
find_prev_version_from_git_tags() {
local variable_name=$1
local current_version=${!variable_name}
local repository=$2
# Normally a "v" is used before the version number, but support alternate cases
local prefix=${3:-"tags/v"}
# Some repositories use "_" instead of "." for version number part separation, support that
local separator=${4:-"."}
# Some tools release versions that omit the last digit (e.g. go)
local last_part_optional=${5:-"false"}
# Some repositories may have tags that include a suffix (e.g. actions/node-versions)
local version_suffix_regex=$6
# Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios.
set +e
major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')"
minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')"
breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')"

if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then
((major=major-1))
declare -g ${variable_name}="${major}"
# Look for latest version from previous major release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
# Handle situations like Go's odd version pattern where "0" releases omit the last part
elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then
((minor=minor-1))
declare -g ${variable_name}="${major}.${minor}"
# Look for latest version from previous minor release
find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}"
else
((breakfix=breakfix-1))
if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then
declare -g ${variable_name}="${major}.${minor}"
else
declare -g ${variable_name}="${major}.${minor}.${breakfix}"
fi
fi
set -e
}

find_sentinel_version_from_url() {
local variable_name=$1
local requested_version=${!variable_name}
Expand Down Expand Up @@ -177,19 +218,94 @@ check_packages() {
fi
}
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved

# Function to fetch the version released prior to the latest version
get_previous_version() {
local url=$1
local repo_url=$2
local variable_name=$3
prev_version=${!variable_name}

output=$(curl -s "$repo_url");
# checking if jq package exists
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
if ! command -v jq &> /dev/null
then
echo "jq could not be found, attempting to install..."
apt-get update && apt-get install -y jq
fi
message=$(echo "$output" | jq -r '.message')

if [[ $message == "API rate limit exceeded"* ]]; then
echo -e "\nAn attempt to find latest version using GitHub Api Failed... \nReason: ${message}"
echo -e "\nAttempting to find latest version using GitHub tags."
find_prev_version_from_git_tags prev_version "$url" "tags/v"
declare -g ${variable_name}="${prev_version}"
else
echo -e "\nAttempting to find latest version using GitHub Api."
version=$(echo "$output" | jq -r '.tag_name')
declare -g ${variable_name}="${version#v}"
fi
echo "${variable_name}=${!variable_name}"
}

get_github_api_repo_url() {
local url=$1
echo "${url/https:\/\/github.com/https:\/\/api.github.com\/repos}/releases/latest"
}

get_pkg_name() {
local input_string="$1"
local lowercase_input="${input_string,,}" # Convert to lowercase
local suffix="_version"
local substring="${lowercase_input%$suffix*}" # Remove suffix and everything after it
echo "$substring"
}

gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
install_previous_version() {
given_version=$1
requested_version=${!given_version}
local URL=$2
local REPO_URL=$(get_github_api_repo_url "$URL")
local PKG_NAME=$(get_pkg_name "${given_version}")
echo -e "\n(!) Failed to fetch the latest artifacts for ${PKG_NAME} v${requested_version}..."
get_previous_version "$URL" "$REPO_URL" requested_version
echo -e "\nAttempting to install ${requested_version}"
declare -g ${given_version}="${requested_version#v}"
INSTALLER_FN="install_${PKG_NAME}"
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
$INSTALLER_FN "${!given_version}"
echo "${given_version}=${!given_version}"
}

install_cosign() {
COSIGN_VERSION=$1
local URL=$2
cosign_filename="/tmp/cosign_${COSIGN_VERSION}_${architecture}.deb"
cosign_url="https://github.com/sigstore/cosign/releases/latest/download/cosign_${COSIGN_VERSION}_${architecture}.deb"
curl -L "${cosign_url}" -o $cosign_filename
if grep -q "Not Found" "$cosign_filename"; then
echo -e "\n(!) Failed to fetch the latest artifacts for cosign v${COSIGN_VERSION}..."
REPO_URL=$(get_github_api_repo_url "$URL")
get_previous_version "$URL" "$REPO_URL" COSIGN_VERSION
echo -e "\nAttempting to install ${COSIGN_VERSION}"
cosign_filename="/tmp/cosign_${COSIGN_VERSION}_${architecture}.deb"
cosign_url="https://github.com/sigstore/cosign/releases/latest/download/cosign_${COSIGN_VERSION}_${architecture}.deb"
curl -L "${cosign_url}" -o $cosign_filename
fi
dpkg -i $cosign_filename
rm $cosign_filename
echo "Installation of cosign succeeded with ${COSIGN_VERSION}."
}

# Install 'cosign' for validating signatures
# https://docs.sigstore.dev/cosign/overview/
ensure_cosign() {
check_packages curl ca-certificates gnupg2

if ! type cosign > /dev/null 2>&1; then
echo "Installing cosign..."
LATEST_COSIGN_VERSION="latest"
find_version_from_git_tags LATEST_COSIGN_VERSION 'https://github.com/sigstore/cosign'
curl -L "https://github.com/sigstore/cosign/releases/latest/download/cosign_${LATEST_COSIGN_VERSION}_${architecture}.deb" -o /tmp/cosign_${LATEST_COSIGN_VERSION}_${architecture}.deb

dpkg -i /tmp/cosign_${LATEST_COSIGN_VERSION}_${architecture}.deb
rm /tmp/cosign_${LATEST_COSIGN_VERSION}_${architecture}.deb
COSIGN_VERSION="latest"
cosign_url='https://github.com/sigstore/cosign'
find_version_from_git_tags COSIGN_VERSION "${cosign_url}"
install_cosign "${COSIGN_VERSION}" "${cosign_url}"
fi
if ! type cosign > /dev/null 2>&1; then
echo "(!) Failed to install cosign."
Expand All @@ -207,18 +323,30 @@ if ! type git > /dev/null 2>&1; then
check_packages git
fi

terraform_url='https://github.com/hashicorp/terraform'
tflint_url='https://github.com/terraform-linters/tflint'
terragrunt_url='https://github.com/gruntwork-io/terragrunt'
# Verify requested version is available, convert latest
find_version_from_git_tags TERRAFORM_VERSION 'https://github.com/hashicorp/terraform'
find_version_from_git_tags TFLINT_VERSION 'https://github.com/terraform-linters/tflint'
find_version_from_git_tags TERRAGRUNT_VERSION 'https://github.com/gruntwork-io/terragrunt'
find_version_from_git_tags TERRAFORM_VERSION "$terraform_url"
find_version_from_git_tags TFLINT_VERSION "$tflint_url"
find_version_from_git_tags TERRAGRUNT_VERSION "$terragrunt_url"

install_terraform() {
local TERRAFORM_VERSION=$1
terraform_filename="terraform_${TERRAFORM_VERSION}_linux_${architecture}.zip"
curl -sSL -o ${terraform_filename} "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/${terraform_filename}"
}

mkdir -p /tmp/tf-downloads
cd /tmp/tf-downloads

# Install Terraform, tflint, Terragrunt
echo "Downloading terraform..."
terraform_filename="terraform_${TERRAFORM_VERSION}_linux_${architecture}.zip"
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
curl -sSL -o ${terraform_filename} "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/${terraform_filename}"
install_terraform "$TERRAFORM_VERSION"
if grep -q "The specified key does not exist." "${terraform_filename}"; then
install_previous_version TERRAFORM_VERSION $terraform_url
terraform_filename="terraform_${TERRAFORM_VERSION}_linux_${architecture}.zip"
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
fi
if [ "${TERRAFORM_SHA256}" != "dev-mode" ]; then
if [ "${TERRAFORM_SHA256}" = "automatic" ]; then
receive_gpg_keys TERRAFORM_GPG_KEY
Expand All @@ -233,10 +361,18 @@ fi
unzip ${terraform_filename}
mv -f terraform /usr/local/bin/

install_tflint() {
TFLINT_VERSION=$1
curl -sSL -o /tmp/tf-downloads/${TFLINT_FILENAME} https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/${TFLINT_FILENAME}
}

if [ "${TFLINT_VERSION}" != "none" ]; then
echo "Downloading tflint..."
TFLINT_FILENAME="tflint_linux_${architecture}.zip"
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
curl -sSL -o /tmp/tf-downloads/${TFLINT_FILENAME} https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/${TFLINT_FILENAME}
install_tflint "$TFLINT_VERSION"
if grep -q "Not Found" "/tmp/tf-downloads/${TFLINT_FILENAME}"; then
install_previous_version TFLINT_VERSION "$tflint_url"
fi
if [ "${TFLINT_SHA256}" != "dev-mode" ]; then

if [ "${TFLINT_SHA256}" != "automatic" ]; then
Expand Down Expand Up @@ -277,10 +413,20 @@ if [ "${TFLINT_VERSION}" != "none" ]; then
unzip /tmp/tf-downloads/${TFLINT_FILENAME}
mv -f tflint /usr/local/bin/
fi

install_terragrunt() {
TERRAGRUNT_VERSION=$1
curl -sSL -o /tmp/tf-downloads/${terragrunt_filename} https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/${terragrunt_filename}
}

if [ "${TERRAGRUNT_VERSION}" != "none" ]; then
echo "Downloading Terragrunt..."
terragrunt_filename="terragrunt_linux_${architecture}"
curl -sSL -o /tmp/tf-downloads/${terragrunt_filename} https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/${terragrunt_filename}
install_terragrunt "$TERRAGRUNT_VERSION"
gauravsaini04 marked this conversation as resolved.
Show resolved Hide resolved
output=$(cat "/tmp/tf-downloads/${terragrunt_filename}")
if [[ $output == "Not Found" ]]; then
install_previous_version TERRAGRUNT_VERSION $terragrunt_url
fi
if [ "${TERRAGRUNT_SHA256}" != "dev-mode" ]; then
if [ "${TERRAGRUNT_SHA256}" = "automatic" ]; then
curl -sSL -o terragrunt_SHA256SUMS https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/SHA256SUMS
Expand Down Expand Up @@ -318,12 +464,23 @@ if [ "${INSTALL_SENTINEL}" = "true" ]; then
mv -f /tmp/tf-downloads/sentinel /usr/local/bin/sentinel
fi

install_tfsec() {
local TFSEC_VERSION=$1
tfsec_filename="tfsec_${TFSEC_VERSION}_linux_${architecture}.tar.gz"
curl -sSL -o /tmp/tf-downloads/${tfsec_filename} https://github.com/aquasecurity/tfsec/releases/download/v${TFSEC_VERSION}/${tfsec_filename}
}

if [ "${INSTALL_TFSEC}" = "true" ]; then
TFSEC_VERSION="latest"
find_version_from_git_tags TFSEC_VERSION 'https://github.com/aquasecurity/tfsec'
tfsec_url='https://github.com/aquasecurity/tfsec'
find_version_from_git_tags TFSEC_VERSION $tfsec_url
tfsec_filename="tfsec_${TFSEC_VERSION}_linux_${architecture}.tar.gz"
echo "(*) Downloading TFSec... ${tfsec_filename}"
curl -sSL -o /tmp/tf-downloads/${tfsec_filename} https://github.com/aquasecurity/tfsec/releases/download/v${TFSEC_VERSION}/${tfsec_filename}
install_tfsec "$TFSEC_VERSION"
if grep -q "Not Found" "/tmp/tf-downloads/${tfsec_filename}"; then
install_previous_version TFSEC_VERSION $tfsec_url
tfsec_filename="tfsec_${TFSEC_VERSION}_linux_${architecture}.tar.gz"
fi
if [ "${TFSEC_SHA256}" != "dev-mode" ]; then
if [ "${TFSEC_SHA256}" = "automatic" ]; then
curl -sSL -o tfsec_SHA256SUMS https://github.com/aquasecurity/tfsec/releases/download/v${TFSEC_VERSION}/tfsec_${TFSEC_VERSION}_checksums.txt
Expand All @@ -338,12 +495,23 @@ if [ "${INSTALL_TFSEC}" = "true" ]; then
mv -f /tmp/tf-downloads/tfsec/tfsec /usr/local/bin/tfsec
fi

install_terraform_docs() {
local TERRAFORM_DOCS_VERSION=$1
tfdocs_filename="terraform-docs-v${TERRAFORM_DOCS_VERSION}-linux-${architecture}.tar.gz"
curl -sSL -o /tmp/tf-downloads/${tfdocs_filename} https://github.com/terraform-docs/terraform-docs/releases/download/v${TERRAFORM_DOCS_VERSION}/${tfdocs_filename}
}

if [ "${INSTALL_TERRAFORM_DOCS}" = "true" ]; then
TERRAFORM_DOCS_VERSION="latest"
find_version_from_git_tags TERRAFORM_DOCS_VERSION 'https://github.com/terraform-docs/terraform-docs'
terraform_docs_url='https://github.com/terraform-docs/terraform-docs'
find_version_from_git_tags TERRAFORM_DOCS_VERSION $terraform_docs_url
tfdocs_filename="terraform-docs-v${TERRAFORM_DOCS_VERSION}-linux-${architecture}.tar.gz"
echo "(*) Downloading Terraform docs... ${tfdocs_filename}"
curl -sSL -o /tmp/tf-downloads/${tfdocs_filename} https://github.com/terraform-docs/terraform-docs/releases/download/v${TERRAFORM_DOCS_VERSION}/${tfdocs_filename}
install_terraform_docs "$TERRAFORM_DOCS_VERSION"
if grep -q "Not Found" "/tmp/tf-downloads/${tfdocs_filename}"; then
install_previous_version TERRAFORM_DOCS_VERSION $terraform_docs_url
tfdocs_filename="terraform-docs-v${TERRAFORM_DOCS_VERSION}-linux-${architecture}.tar.gz"
fi
if [ "${TERRAFORM_DOCS_SHA256}" != "dev-mode" ]; then
if [ "${TERRAFORM_DOCS_SHA256}" = "automatic" ]; then
curl -sSL -o tfdocs_SHA256SUMS https://github.com/terraform-docs/terraform-docs/releases/download/v${TERRAFORM_DOCS_VERSION}/terraform-docs-v${TERRAFORM_DOCS_VERSION}.sha256sum
Expand Down
40 changes: 40 additions & 0 deletions test/terraform/scenarios.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
}
}
},
"tfsec_fallback_test": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"installTFsec": true
}
}
},
"install_terraform_docs": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
Expand All @@ -23,6 +31,38 @@
}
}
},
"terraform_docs_fallback_test": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"installTerraformDocs": true
}
}
},
"terraform_fallback_test": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"version": "latest"
}
}
},
"terragrunt_fallback_test": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"terragrunt": "latest"
}
}
},
"tflint_fallback_test": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
"terraform": {
"tflint": "latest"
}
}
},
"older_tflint": {
"image": "mcr.microsoft.com/devcontainers/base:jammy",
"features": {
Expand Down
Loading
Loading