diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 375690a..c68bad5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,37 +1,68 @@ -# Note: You can use any Debian/Ubuntu based image you want. -FROM docker.io/golang:1.22.7-bullseye +# Terraform Provider Utilities Development Container +FROM docker.io/golang:1.24-bullseye # [Option] Install zsh ARG INSTALL_ZSH="true" # [Option] Upgrade OS packages to their latest versions -ARG UPGRADE_PACKAGES="false" -# [Option] Enable non-root Docker access in container -ARG ENABLE_NONROOT_DOCKER="true" -# [Option] Use the OSS Moby Engine instead of the licensed Docker Engine -ARG USE_MOBY="true" -# [Option] Engine/CLI Version -ARG DOCKER_VERSION="latest" +ARG UPGRADE_PACKAGES="true" -# Enable new "BUILDKIT" mode for Docker CLI -ENV DOCKER_BUILDKIT=1 - -# Install needed packages and setup non-root user. Use a separate RUN statement to add your -# own dependencies. A user of "automatic" attempts to reuse an user ID if one already exists. -ARG USERNAME=automatic +# Install needed packages and setup non-root user +ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=$USER_UID + COPY scripts/*.sh /tmp/scripts/ -RUN apt-get update -y \ +COPY bashrc /tmp/bashrc +RUN apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && echo "Acquire::Check-Valid-Until false;" > /etc/apt/apt.conf.d/99no-check-valid-until \ + && (apt-get update -y --allow-releaseinfo-change 2>&1 || true) \ + && apt-get install -y --allow-unauthenticated ca-certificates gnupg lsb-release || true \ + && apt-get update -y --allow-releaseinfo-change \ && /bin/bash /tmp/scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \ - # Use Docker script from script library to set things up - && /bin/bash /tmp/scripts/docker-in-docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "${USERNAME}" "${USE_MOBY}" "${DOCKER_VERSION}" \ + # Install additional development tools + && apt-get install -y \ + git \ + curl \ + wget \ + make \ + ca-certificates \ + gnupg \ + lsb-release \ + software-properties-common \ + apt-transport-https \ + bash-completion \ + # Install golangci-lint (latest) + && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin \ + # Install goimports (latest - GOTOOLCHAIN=auto will download compatible Go toolchain if needed) + && GOTOOLCHAIN=auto go install golang.org/x/tools/cmd/goimports@latest \ + # Install gopls (latest - GOTOOLCHAIN=auto will download compatible Go toolchain if needed) + && GOTOOLCHAIN=auto go install golang.org/x/tools/gopls@latest \ + # Configure bash history and completion for root + && cat /tmp/bashrc >> /root/.bashrc \ + # Configure bash history and completion for vscode user if it exists + && if [ -d /home/${USERNAME} ]; then cat /tmp/bashrc >> /home/${USERNAME}/.bashrc; fi \ + # Also add .env loading to system-wide bashrc for VS Code terminals + && echo "" >> /etc/bash.bashrc \ + && echo "# Auto-load .env file if it exists (for Utilities token and Terraform variables)" >> /etc/bash.bashrc \ + && echo "if [ -f /workspaces/terraform-provider-utilities/.env ]; then" >> /etc/bash.bashrc \ + && echo " set -a" >> /etc/bash.bashrc \ + && echo " source /workspaces/terraform-provider-utilities/.env" >> /etc/bash.bashrc \ + && echo " set +a" >> /etc/bash.bashrc \ + && echo "elif [ -f /workspace/.env ]; then" >> /etc/bash.bashrc \ + && echo " set -a" >> /etc/bash.bashrc \ + && echo " source /workspace/.env" >> /etc/bash.bashrc \ + && echo " set +a" >> /etc/bash.bashrc \ + && echo "fi" >> /etc/bash.bashrc \ # Clean up - && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/scripts/ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/scripts/ /tmp/bashrc + +# Set up Go environment +ENV GOPATH=/go +ENV PATH=$GOPATH/bin:$PATH -VOLUME [ "/var/lib/docker" ] +# Keep as root user to avoid permission issues +# USER ${USERNAME} -# Setting the ENTRYPOINT to docker-init.sh will start up the Docker Engine -# inside the container "overrideCommand": false is set in devcontainer.json. -# The script will also execute CMD if you need to alter startup behaviors. -ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] -CMD [ "sleep", "infinity" ] +# Set working directory +WORKDIR /workspace diff --git a/.devcontainer/bashrc b/.devcontainer/bashrc new file mode 100644 index 0000000..5fed864 --- /dev/null +++ b/.devcontainer/bashrc @@ -0,0 +1,38 @@ +# Enable bash history with arrow key navigation +export HISTSIZE=10000 +export HISTFILESIZE=20000 +export HISTCONTROL=ignoredups:erasedups +# Append to history file instead of overwriting +shopt -s histappend +# Save history after each command +export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND" + +# Enable arrow key history navigation +bind '"\e[A": history-search-backward' +bind '"\e[B": history-search-forward' +bind '"\e[C": forward-char' +bind '"\e[D": backward-char' + +# Enable bash completion +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +# Enable completion for common commands +complete -cf sudo +complete -cf man + +# Auto-load .env file if it exists +if [ -f /workspaces/terraform-provider-utilities/.env ]; then + set -a + source /workspaces/terraform-provider-utilities/.env + set +a +elif [ -f /workspace/.env ]; then + set -a + source /workspace/.env + set +a +fi diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5583013..1874a19 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,22 +1,36 @@ { - "name": "Docker in Docker Development Container Template", + "name": "Terraform Provider Utilities Development", "dockerFile": "Dockerfile", - "runArgs": ["--init", "--privileged"], - "mounts": ["source=dind-var-lib-docker,target=/var/lib/docker,type=volume"], - "overrideCommand": false, + "runArgs": ["--init"], "customizations": { "vscode": { "extensions": [ - "esbenp.prettier-vscode", + "davidanson.vscode-markdownlint", "golang.go", - "Gruntfuggly.todo-tree", "hashicorp.terraform", + "Gruntfuggly.todo-tree", "ms-azuretools.vscode-docker", - "vscode-icons-team.vscode-icons" + "vscode-icons-team.vscode-icons", + "ms-vscode.makefile-tools" ], "settings": { - "workbench.iconTheme": "vscode-icons" + "workbench.iconTheme": "vscode-icons", + "go.useLanguageServer": true, + "go.formatTool": "goimports", + "go.lintTool": "golangci-lint", + "go.lintOnSave": "package", + "[go]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "[terraform]": { + "editor.formatOnSave": true + }, + "terraform.format.enable": true, + "terraform.languageServer.enable": true } } }, @@ -27,6 +41,6 @@ // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": ".devcontainer/scripts/postCreate.sh" - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - // "remoteUser": "vscode" + // Connect as root to avoid permission issues (commented out remoteUser means root) + // "remoteUser": "vscode" } diff --git a/.devcontainer/scripts/docker-in-docker-debian.sh b/.devcontainer/scripts/docker-in-docker-debian.sh deleted file mode 100644 index 88603a9..0000000 --- a/.devcontainer/scripts/docker-in-docker-debian.sh +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/env bash -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- -# -# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md -# Maintainer: The VS Code and Codespaces Teams -# -# Syntax: ./docker-in-docker-debian.sh [enable non-root docker access flag] [non-root user] [use moby] [Engine/CLI Version] [Major version for docker-compose] [azure DNS auto detection flag] - -ENABLE_NONROOT_DOCKER=${1:-"true"} -USERNAME=${2:-"automatic"} -USE_MOBY=${3:-"true"} -DOCKER_VERSION=${4:-"latest"} # The Docker/Moby Engine + CLI should match in version -DOCKER_DASH_COMPOSE_VERSION=${5:-"v1"} # v1 or v2 -AZURE_DNS_AUTO_DETECTION=${6:-"true"} -MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" -DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy" -DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy" - -# Default: Exit on any failure. -set -e - -# Setup STDERR. -err() { - echo "(!) $*" >&2 -} - -if [ "$(id -u)" -ne 0 ]; then - err 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' - exit 1 -fi - -################### -# Helper Functions -# See: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/shared/utils.sh -################### - -# Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in ${POSSIBLE_USERS[@]}; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi - -# Get central common setting -get_common_setting() { - if [ "${common_settings_file_loaded}" != "true" ]; then - curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping." - common_settings_file_loaded=true - fi - if [ -f "/tmp/vsdc-settings.env" ]; then - local multi_line="" - if [ "$2" = "true" ]; then multi_line="-z"; fi - local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')" - if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi - fi - echo "$1=${!1}" -} - -# Function to run apt-get if needed -apt_get_update_if_needed() -{ - if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then - echo "Running apt-get update..." - apt-get update - else - echo "Skipping apt-get update." - fi -} - -# Checks if packages are installed and installs them if not -check_packages() { - if ! dpkg -s "$@" > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install --no-install-recommends "$@" - fi -} - -# Figure out correct version of a three part version number is not passed -find_version_from_git_tags() { - local variable_name=$1 - local requested_version=${!variable_name} - if [ "${requested_version}" = "none" ]; then return; fi - local repository=$2 - local prefix=${3:-"tags/v"} - local separator=${4:-"."} - local last_part_optional=${5:-"false"} - if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then - local escaped_separator=${separator//./\\.} - local last_part - if [ "${last_part_optional}" = "true" ]; then - last_part="(${escaped_separator}[0-9]+)?" - else - last_part="${escaped_separator}[0-9]+" - fi - local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" - local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" - if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then - declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" - else - set +e - declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" - set -e - fi - fi - if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then - err "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 - exit 1 - fi - echo "${variable_name}=${!variable_name}" -} - -########################################### -# Start docker-in-docker installation -########################################### - -# Ensure apt is in non-interactive to avoid prompts -export DEBIAN_FRONTEND=noninteractive - - -# Source /etc/os-release to get OS info -. /etc/os-release -# Fetch host/container arch. -architecture="$(dpkg --print-architecture)" - -# Check if distro is suppported -if [ "${USE_MOBY}" = "true" ]; then - # 'get_common_setting' allows attribute to be updated remotely - get_common_setting DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES - if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then - err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution" - err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" - exit 1 - fi - echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'" -else - get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES - if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then - err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution" - err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" - exit 1 - fi - echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}'" -fi - -# Install dependencies -check_packages apt-transport-https curl ca-certificates pigz iptables gnupg2 dirmngr -if ! type git > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install git -fi - -# Swap to legacy iptables for compatibility -if type iptables-legacy > /dev/null 2>&1; then - update-alternatives --set iptables /usr/sbin/iptables-legacy - update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy -fi - - - -# Set up the necessary apt repos (either Microsoft's or Docker's) -if [ "${USE_MOBY}" = "true" ]; then - - # Name of open source engine/cli - engine_package_name="moby-engine" - cli_package_name="moby-cli" - - # Import key safely and import Microsoft apt repo - get_common_setting MICROSOFT_GPG_KEYS_URI - curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg - echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list -else - # Name of licensed engine/cli - engine_package_name="docker-ce" - cli_package_name="docker-ce-cli" - - # Import key safely and import Docker apt repo - curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list -fi - -# Refresh apt lists -apt-get update - -# Soft version matching -if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then - # Empty, meaning grab whatever "latest" is in apt repo - engine_version_suffix="" - cli_version_suffix="" -else - # Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...) - docker_version_dot_escaped="${DOCKER_VERSION//./\\.}" - docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}" - # Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/ - docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)" - set +e # Don't exit if finding version fails - will handle gracefully - cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" - engine_version_suffix="=$(apt-cache madison ${engine_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" - set -e - if [ -z "${engine_version_suffix}" ] || [ "${engine_version_suffix}" = "=" ] || [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ] ; then - err "No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:" - apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+' - exit 1 - fi - echo "engine_version_suffix ${engine_version_suffix}" - echo "cli_version_suffix ${cli_version_suffix}" -fi - -# Install Docker / Moby CLI if not already installed -if type docker > /dev/null 2>&1 && type dockerd > /dev/null 2>&1; then - echo "Docker / Moby CLI and Engine already installed." -else - if [ "${USE_MOBY}" = "true" ]; then - # Install engine - set +e # Handle error gracefully - apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx moby-engine${engine_version_suffix} - if [ $? -ne 0 ]; then - err "Packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-20.04')." - exit 1 - fi - set -e - - # Install compose - apt-get -y install --no-install-recommends moby-compose || err "Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." - else - apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} docker-ce${engine_version_suffix} - # Install compose - apt-get -y install --no-install-recommends docker-compose-plugin || echo "(*) Package docker-compose-plugin (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." - fi -fi - -echo "Finished installing docker / moby!" - -# Install Docker Compose if not already installed and is on a supported architecture -if type docker-compose > /dev/null 2>&1; then - echo "Docker Compose v1 already installed." -else - target_compose_arch="${architecture}" - if [ "${target_compose_arch}" = "amd64" ]; then - target_compose_arch="x86_64" - fi - if [ "${target_compose_arch}" != "x86_64" ]; then - # Use pip to get a version that runs on this architecture - if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install python3-minimal python3-pip libffi-dev python3-venv - fi - export PIPX_HOME=/usr/local/pipx - mkdir -p ${PIPX_HOME} - export PIPX_BIN_DIR=/usr/local/bin - export PYTHONUSERBASE=/tmp/pip-tmp - export PIP_CACHE_DIR=/tmp/pip-tmp/cache - pipx_bin=pipx - if ! type pipx > /dev/null 2>&1; then - pip3 install --disable-pip-version-check --no-cache-dir --user pipx - pipx_bin=/tmp/pip-tmp/bin/pipx - fi - ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose - rm -rf /tmp/pip-tmp - else - compose_v1_version="1" - find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/" - echo "(*) Installing docker-compose ${compose_v1_version}..." - curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose - fi -fi - -# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation -current_v1_compose_path="$(which docker-compose)" -target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1" -if ! type compose-switch > /dev/null 2>&1; then - echo "(*) Installing compose-switch..." - compose_switch_version="latest" - find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch" - curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch - chmod +x /usr/local/bin/compose-switch - # TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11 - - # Setup v1 CLI as alternative in addition to compose-switch (which maps to v2) - mv "${current_v1_compose_path}" "${target_v1_compose_path}" - update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99 - update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1 -fi -if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then - update-alternatives --set docker-compose "${target_v1_compose_path}" -else - update-alternatives --set docker-compose /usr/local/bin/compose-switch -fi - -# If init file already exists, exit -if [ -f "/usr/local/share/docker-init.sh" ]; then - echo "/usr/local/share/docker-init.sh already exists, so exiting." - exit 0 -fi -echo "docker-init doesnt exist, adding..." - -# Add user to the docker group -if [ "${ENABLE_NONROOT_DOCKER}" = "true" ]; then - if ! getent group docker > /dev/null 2>&1; then - groupadd docker - fi - - usermod -aG docker ${USERNAME} -fi - -tee /usr/local/share/docker-init.sh > /dev/null \ -<< EOF -#!/bin/sh -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -set -e - -AZURE_DNS_AUTO_DETECTION=$AZURE_DNS_AUTO_DETECTION -EOF - -tee -a /usr/local/share/docker-init.sh > /dev/null \ -<< 'EOF' -dockerd_start="$(cat << 'INNEREOF' - # explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly - # ie: docker kill - find /run /var/run -iname 'docker*.pid' -delete || : - find /run /var/run -iname 'container*.pid' -delete || : - - ## Dind wrapper script from docker team, adapted to a function - # Maintained: https://github.com/moby/moby/blob/master/hack/dind - - export container=docker - - if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then - mount -t securityfs none /sys/kernel/security || { - echo >&2 'Could not mount /sys/kernel/security.' - echo >&2 'AppArmor detection and --privileged mode might break.' - } - fi - - # Mount /tmp (conditionally) - if ! mountpoint -q /tmp; then - mount -t tmpfs none /tmp - fi - - # cgroup v2: enable nesting - if [ -f /sys/fs/cgroup/cgroup.controllers ]; then - # move the processes from the root group to the /init group, - # otherwise writing subtree_control fails with EBUSY. - # An error during moving non-existent process (i.e., "cat") is ignored. - mkdir -p /sys/fs/cgroup/init - xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : - # enable controllers - sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ - > /sys/fs/cgroup/cgroup.subtree_control - fi - ## Dind wrapper over. - - # Handle DNS - set +e - cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' - if [ $? -eq 0 ] && [ ${AZURE_DNS_AUTO_DETECTION} = "true" ] - then - echo "Setting dockerd Azure DNS." - CUSTOMDNS="--dns 168.63.129.16" - else - echo "Not setting dockerd DNS manually." - CUSTOMDNS="" - fi - set -e - - # Start docker/moby engine - ( dockerd $CUSTOMDNS > /tmp/dockerd.log 2>&1 ) & -INNEREOF -)" - -# Start using sudo if not invoked as root -if [ "$(id -u)" -ne 0 ]; then - sudo /bin/sh -c "${dockerd_start}" -else - eval "${dockerd_start}" -fi - -set +e - -# Execute whatever commands were passed in (if any). This allows us -# to set this script to ENTRYPOINT while still executing the default CMD. -exec "$@" -EOF - -chmod +x /usr/local/share/docker-init.sh -chown ${USERNAME}:root /usr/local/share/docker-init.sh - -echo 'docker-in-docker-debian script has completed!' \ No newline at end of file diff --git a/.devcontainer/scripts/postCreate.sh b/.devcontainer/scripts/postCreate.sh index 26d0c62..6d8e5c6 100755 --- a/.devcontainer/scripts/postCreate.sh +++ b/.devcontainer/scripts/postCreate.sh @@ -1,22 +1,94 @@ -# Include commands that you would like to execute after the container is created +#!/bin/bash +set -e + +echo "🚀 Setting up Terraform Provider Utilities development environment..." + +# Display system information +echo "📋 System Information:" uname -a +echo "Go version: $(go version)" +echo "Go path: $(go env GOPATH)" -apt-get update -y +# Install Terraform +echo "📦 Installing Terraform..." export DEBIAN_FRONTEND=noninteractive - -# terraform +apt-get update -y apt-get install -y apt-utils gnupg software-properties-common -curl -s https://apt.releases.hashicorp.com/gpg | gpg --dearmor > hashicorp.gpg -install -o root -g root -m 644 hashicorp.gpg /etc/apt/trusted.gpg.d/ -apt-add-repository -y "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" + +curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list apt-get update -y apt-get install -y terraform -rm hashicorp.gpg -# install golangci-lint -curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.0 +echo "✅ Terraform installed: $(terraform version)" + +# Install Terraform Plugin Framework docs generator +echo "📚 Installing Terraform Plugin Framework documentation generator..." +go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@latest + +# Verify Go tools +echo "🔧 Verifying Go tools..." +echo "golangci-lint: $(golangci-lint version)" +echo "goimports: $(which goimports)" +echo "gopls: $(which gopls)" + +# Download Go dependencies +echo "📥 Downloading Go dependencies..." +cd /workspaces/terraform-provider-utilities || cd /workspace +go mod download +go mod verify + +# Build the provider to verify everything works +echo "🔨 Building provider..." +go build -buildvcs=false -o terraform-provider-utilities + +# Install provider locally for Terraform to use +echo "📦 Installing provider locally for Terraform..." +VERSION="0.1.0" +PLATFORM="linux_amd64" +PLUGIN_DIR="${HOME}/.terraform.d/plugins/registry.terraform.io/tfstack/utilities/${VERSION}/${PLATFORM}" +mkdir -p "${PLUGIN_DIR}" +cp terraform-provider-utilities "${PLUGIN_DIR}/" +echo "✅ Provider installed to ${PLUGIN_DIR}" + +# Initialize Terraform in examples (non-blocking, may fail if variables needed) +echo "🔧 Initializing Terraform examples..." +for dir in examples/data-sources/*/ examples/resources/*/ examples/provider/; do + if [ -f "${dir}data-source.tf" ] || [ -f "${dir}resource.tf" ] || [ -f "${dir}provider.tf" ] || [ -f "${dir}main.tf" ] || [ -f "${dir}"*.tf ]; then + echo " Initializing ${dir}..." + cd "${dir}" && terraform init -upgrade > /dev/null 2>&1 && echo " ✅ ${dir} initialized" || echo " ⚠️ ${dir} skipped (may need variables)" + cd - > /dev/null + fi + done + +# Load .env file if it exists +echo "🔐 Loading environment variables from .env file..." +if [ -f /workspaces/terraform-provider-utilities/.env ]; then + set -a + source /workspaces/terraform-provider-utilities/.env + set +a + echo "✅ Environment variables loaded from .env" +elif [ -f /workspace/.env ]; then + set -a + source /workspace/.env + set +a + echo "✅ Environment variables loaded from .env" +else + echo "⚠️ No .env file found. Create one from .env.example if needed." +fi -# install tfplugindocs -export GOBIN=$PWD/bin -export PATH=$GOBIN:$PATH -go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs +echo "" +echo "✅ Development environment setup complete!" +echo "" +echo "Available commands:" +echo " make build - Build the provider" +echo " make install - Install the provider" +echo " make install-local - Install provider locally for Terraform testing" +echo " make init-examples - Initialize Terraform in all examples" +echo " make init-example - Initialize a specific example (EXAMPLE=path)" +echo " make test - Run tests" +echo " make fmt - Format code" +echo " make docs - Generate documentation" +echo "" +echo "💡 The provider is already installed locally and examples are initialized!" +echo " Navigate to any example directory and run 'terraform plan' or 'terraform apply'." diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d38fd88..68f13e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,13 @@ # Terraform Provider release workflow. name: Release -# This GitHub action creates a release when a tag that matches the pattern -# "v*" (e.g. v0.1.0) is created. on: - push: - tags: - - 'v*' + workflow_run: + workflows: ["Terraform Tag"] + types: + - completed + branches: + - main # Releases need permissions to read and write the repository contents. # GitHub considers creating releases and uploading assets as writing contents. @@ -15,12 +16,15 @@ permissions: jobs: goreleaser: + if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: # Allow goreleaser to access older tag information. fetch-depth: 0 + # Checkout the commit from the triggering workflow run + ref: ${{ github.event.workflow_run.head_sha }} - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: 'go.mod' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index adfcbd9..b19d203 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,6 +70,9 @@ jobs: - '1.2.*' - '1.3.*' - '1.4.*' + - '1.5.*' + - '1.6.*' + - '1.7.*' steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 diff --git a/.gitignore b/.gitignore index fd3ad8e..d422298 100644 --- a/.gitignore +++ b/.gitignore @@ -1,35 +1,55 @@ -*.dll +# Binaries for programs and plugins *.exe -.DS_Store -example.tf -terraform.tfplan -terraform.tfstate -bin/ -dist/ -modules-dev/ -/pkg/ -website/.vagrant -website/.bundle -website/build -website/node_modules -.vagrant/ -*.backup -./*.tfstate +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool +*.out +coverage.html + +# Dependency directories +vendor/ + +# Go workspace file +go.work + +# Terraform files .terraform/ -*.log -*.bak +.terraform.lock.hcl +*.tfstate +*.tfstate.* +crash.log +crash.*.log +override.tf +override.tf.json +*_override.tf +*_override.tf.json +.terraformrc +terraform.rc + +# Example state files +examples/**/*.tfstate +examples/**/*.tfstate.* +examples/**/.terraform/ + +# IDE files +.idea/ +*.swp +*.swo *~ -.*.swp -.idea -*.iml -*.test -*.iml -website/vendor +# OS files +.DS_Store +Thumbs.db -# Test exclusions -!command/test-fixtures/**/*.tfstate -!command/test-fixtures/**/.terraform/ +# Provider binary +terraform-provider-discord +terraform-provider-discord.exe -# Keep windows files with windows line endings -*.winfile eol=crlf +# Environment variables +.env diff --git a/GNUmakefile b/GNUmakefile index e339a0a..2fb7f93 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,24 +1,127 @@ -default: fmt lint install generate +.PHONY: build install test test-coverage testacc fmt docs clean test-e2e +# Default target +.DEFAULT_GOAL := help + +# Build the provider build: - go build -v ./... + @echo "==> Building the provider..." + go build -buildvcs=false -o terraform-provider-utilities +# Install the provider install: build - go install -v ./... - -lint: - golangci-lint run + @echo "==> Installing the provider..." + go install -generate: - cd tools; go generate ./... +# Install provider locally for Terraform to use +install-local: build + @echo "==> Installing provider locally for Terraform..." + @VERSION="0.1.0" \ + PLATFORM="linux_amd64" \ + PLUGIN_DIR_REGISTRY="$$HOME/.terraform.d/plugins/registry.terraform.io/tfstack/utilities/$$VERSION/$$PLATFORM" \ + PLUGIN_DIR_SOURCE="$$HOME/.terraform.d/plugins/hashicorp.com/tfstack/utilities/$$VERSION/$$PLATFORM" \ + && mkdir -p "$$PLUGIN_DIR_REGISTRY" "$$PLUGIN_DIR_SOURCE" \ + && cp terraform-provider-utilities "$$PLUGIN_DIR_REGISTRY/" \ + && cp terraform-provider-utilities "$$PLUGIN_DIR_SOURCE/" \ + && echo "✅ Provider installed to $$PLUGIN_DIR_REGISTRY" \ + && echo "✅ Provider installed to $$PLUGIN_DIR_SOURCE" -fmt: - gofmt -s -w -e . +# Setup .terraformrc for local development +setup-terraformrc: install-local + @echo "==> Setting up .terraformrc for local development..." + @mkdir -p $$HOME/.terraform.d/plugins/hashicorp.com/tfstack/utilities/0.1.0/linux_amd64 + @echo 'provider_installation {' > $$HOME/.terraformrc + @echo ' dev_overrides {' >> $$HOME/.terraformrc + @echo ' "hashicorp.com/tfstack/utilities" = "$$HOME/.terraform.d/plugins/hashicorp.com/tfstack/utilities/0.1.0/linux_amd64"' >> $$HOME/.terraformrc + @echo ' }' >> $$HOME/.terraformrc + @echo ' direct {}' >> $$HOME/.terraformrc + @echo '}' >> $$HOME/.terraformrc + @echo "✅ .terraformrc configured" + @echo "" + @echo "⚠️ IMPORTANT: Skip 'terraform init' when using dev_overrides!" + @echo " Use 'terraform plan' or 'terraform apply' directly." +# Run tests test: - go test -v -cover -timeout=120s -parallel=10 ./... + @echo "==> Running tests..." + go test -v ./... +# Run tests with coverage +test-coverage: + @echo "==> Running tests with coverage..." + go test -coverprofile=coverage.out ./... + @echo "==> Coverage report:" + @go tool cover -func=coverage.out + @echo "" + @echo "==> HTML coverage report generated: coverage.html" + @go tool cover -html=coverage.out -o coverage.html + +# Run acceptance tests testacc: - TF_ACC=1 go test -v -cover -timeout 120m ./... + @echo "==> Running acceptance tests..." + TF_ACC=1 go test -v ./... + +# Format code +fmt: + @echo "==> Formatting code..." + go fmt ./... + terraform fmt -recursive ./examples/ + +# Generate documentation +docs: + @echo "==> Generating documentation..." + go generate ./... + +# Initialize Terraform in all examples (skip init when using dev_overrides) +init-examples: setup-terraformrc + @echo "==> Examples ready (skip terraform init when using dev_overrides)" + @echo " Use: cd examples/data-sources/utilities_bcrypt_hash && terraform plan" + +# End-to-end test for bcrypt hash +test-e2e: setup-terraformrc + @echo "==> Running end-to-end test for bcrypt hash..." + @cd examples/data-sources/utilities_bcrypt_hash && \ + rm -rf .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup 2>/dev/null || true && \ + terraform plan -out=tfplan && \ + terraform apply tfplan && \ + terraform show -json | grep -q '"hash"' && echo "✅ E2E test passed" || (echo "❌ E2E test failed" && exit 1) + +# Initialize a specific example +init-example: install-local + @if [ -z "$(EXAMPLE)" ]; then \ + echo "Usage: make init-example EXAMPLE=examples/data-sources/utilities_channels"; \ + exit 1; \ + fi + @echo "==> Initializing $(EXAMPLE)..." + @cd $(EXAMPLE) && terraform init -upgrade + +# Clean build artifacts +clean: + @echo "==> Cleaning..." + rm -f terraform-provider-utilities + rm -f terraform-provider-utilities.exe + rm -f coverage.out coverage.html + go clean + @echo "==> Cleaning Terraform state files..." + @find examples -name ".terraform" -type d -exec rm -rf {} + 2>/dev/null || true + @find examples -name ".terraform.lock.hcl" -type f -delete 2>/dev/null || true + @find examples -name "*.tfstate" -type f -delete 2>/dev/null || true + @find examples -name "*.tfstate.*" -type f -delete 2>/dev/null || true -.PHONY: fmt lint test testacc build install generate +# Help target +help: + @echo "Available targets:" + @echo " build - Build the provider binary" + @echo " install - Install the provider to GOPATH/bin" + @echo " install-local - Install provider locally for Terraform testing" + @echo " setup-terraformrc - Install provider and configure .terraformrc for local dev" + @echo " init-examples - Examples ready (skip terraform init with dev_overrides)" + @echo " init-example - Initialize a specific example (use EXAMPLE=path)" + @echo " test - Run unit tests" + @echo " test-coverage - Run tests with coverage report" + @echo " testacc - Run acceptance tests (requires TF_ACC=1)" + @echo " test-e2e - Run end-to-end validation test for bcrypt hash" + @echo " fmt - Format code" + @echo " docs - Generate documentation" + @echo " clean - Clean build artifacts and Terraform state" + @echo " help - Show this help message"