diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..8ba436f --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,53 @@ +--- +########################### +########################### +## Linter GitHub Actions ## +########################### +########################### +name: Lint Code Base + +# +# Documentation: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions +# + +############################# +# Start the job on all push # +############################# +on: + push: + branches-ignore: [master] + # Remove the line above to run when pushing to master + pull_request: + branches: [master] + +############### +# Set the Job # +############### +jobs: + build: + # Name the Job + name: Lint Code Base + # Set the agent to run on + runs-on: ubuntu-latest + + ################## + # Load all steps # + ################## + steps: + ########################## + # Checkout the code base # + ########################## + - name: Checkout Code + uses: actions/checkout@v2 + + ################################ + # Run Linter against code base # + ################################ + - name: Lint Code Base + uses: github/super-linter@v3 + env: + VALIDATE_ALL_CODEBASE: false + LINTER_RULES_PATH: / + DEFAULT_BRANCH: master + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 20657c1..5e26057 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -8,7 +8,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Publish to Docker Hub - uses: elgohr/Publish-Docker-Github-Action@2.7 + uses: elgohr/Publish-Docker-Github-Action@v5 with: name: stefanprodan/hrval username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.hadolint.yml b/.hadolint.yml new file mode 100644 index 0000000..0464523 --- /dev/null +++ b/.hadolint.yml @@ -0,0 +1,2 @@ +ignored: + - DL3018 diff --git a/Dockerfile b/Dockerfile index c39c7f3..a414fa0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM garethr/kubeval:latest +FROM garethr/kubeval:0.15.0 -RUN apk --no-cache add curl bash git +RUN apk --no-cache add curl bash git openssh-client COPY LICENSE README.md / diff --git a/README.md b/README.md index 1c1ff25..06b0ff6 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ ![CI](https://github.com/stefanprodan/hrval-action/workflows/CI/badge.svg) [![Docker](https://img.shields.io/badge/Docker%20Hub-stefanprodan%2Fhrval-blue)](https://hub.docker.com/r/stefanprodan/hrval) +[![GitHub Super-Linter](https://github.com/stefanprodan/hrval-action/workflows/Lint%20Code%20Base/badge.svg)](https://github.com/marketplace/actions/super-linter) -This GitHub action validates a Flux +This GitHub action validates a Flux [Helm Release](https://docs.fluxcd.io/projects/helm-operator/en/latest/references/helmrelease-custom-resource.html) Kubernetes custom resources with [kubeval](https://github.com/instrumenta/kubeval). @@ -30,17 +31,17 @@ jobs: steps: - uses: actions/checkout@v1 - name: Validate Helm Releases in test dir - uses: stefanprodan/hrval-action@v3.1.0 + uses: stefanprodan/hrval-action@master with: helmRelease: test/ - name: Validate Helm Release from Helm Repo - uses: stefanprodan/hrval-action@v3.1.0 + uses: stefanprodan/hrval-action@master with: helmRelease: test/flagger.yaml helmVersion: v2 kubernetesVersion: 1.17.0 - name: Validate Helm Release from Git Repo - uses: stefanprodan/hrval-action@v3.1.0 + uses: stefanprodan/hrval-action@master with: helmRelease: test/podinfo.yaml helmVersion: v3 @@ -75,7 +76,10 @@ PASS - flagger/templates/deployment.yaml contains a valid Deployment ## Usage with private charts repositories -To allow the action to be able to clone private charts repositories, you must [create a GitHub private access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) and [add it as a secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) to the target repository. NOTE: secret names *cannot* start with `GITHUB_` as these are reserved. +### Private GitHub/GitLab repository +To allow the action to be able to clone charts from private GitHub repositories, +you must [create a GitHub private access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) +and [add it as a secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) to the target repository. NOTE: secret names *cannot* start with `GITHUB_` as these are reserved. You can then pass the secret (in this case, `GH_TOKEN`) into the action like so: ```yaml @@ -89,13 +93,17 @@ jobs: steps: - uses: actions/checkout@v1 - name: Validate Helm Releases in test dir - uses: stefanprodan/hrval-action@v3.1.0 + uses: stefanprodan/hrval-action@master with: helmRelease: test/ env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} ``` +Gitlab CI Token is also possible using `GITLAB_CI_TOKEN`. + +### AWS S3 + If you set `awsS3Repo: true`, make sure you set the appropriate environment variables for helm s3 plugin to work. Example: ```yaml name: CI @@ -108,7 +116,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Validate Helm Releases in test dir - uses: stefanprodan/hrval-action@v3.1.0 + uses: stefanprodan/hrval-action@master with: helmRelease: test/ awsS3Repo: true @@ -122,11 +130,107 @@ jobs: ``` -Gitlab CI Token is also possible using `GITLAB_CI_TOKEN`. +### HTTP(S) Helm chart repository + +To allow fetching Helm charts from private Helm chart repositories you need to +pass a list of Helm repositories in `HTTP_PRIVATE_CHART_REPOS` environment variable as JSON. + +```json +{ + "repositories": [ + { + "url": "https://raw.githubusercontent.com/username/helm-chart-repository/master/", + "username": "YOUR_USERNAME", + "password": "YOUR_PASSWORD" + }, + { + "url": "https://raw.githubusercontent.com/username/another-helm-chart-repository/master/", + "username": "YOUR_USERNAME", + "password": "YOUR_PASSWORD" + } + ] +} +``` + +It should be passed [as a secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets) +to keep credentials secure. + +```yaml +name: CI + +on: [push, pull_request] + +jobs: + hrval: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Validate Helm Releases in test dir + uses: stefanprodan/hrval-action@master + with: + helmRelease: test/ + env: + HTTP_PRIVATE_CHART_REPOS: ${{ secrets.HTTP_PRIVATE_CHART_REPOS }} +``` + + +## Usage with pull requests containing changes of Helm chart source located in base repository branch + +If a base repository branch of pull request is referenced in helm release, +you need to pass `HRVAL_BASE_BRANCH` and `HRVAL_HEAD_BRANCH` environment variables +to an action to make sure it will check out amended version of the chart +from a head repository branch. + + +```yaml +name: CI + +on: [pull_request] + +jobs: + hrval: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Validate Helm Releases in test dir + uses: stefanprodan/hrval-action@master + with: + helmRelease: test/ + env: + HRVAL_BASE_BRANCH: ${{ github.base_ref }} + HRVAL_HEAD_BRANCH: ${{ github.head_ref }} +``` + +## Usage with Helm source caching enabled + +Sometimes single Helm release might be referenced multiple times in a single Flux repository, +for example if staging branch of Helm chart repository is used as a release ref across all staging releases. +A property named `helmSourcesCacheEnabled` enables caching for such releases, +so a single Helm repository chart version or Git repository ref +will be retrieved only once, and cached version will be used for validation of another releases which reuse same sources. + + +```yaml +name: CI + +on: [pull_request] + +jobs: + hrval: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Validate Helm Releases in test dir + uses: stefanprodan/hrval-action@master + with: + helmRelease: test/ + helmSourcesCacheEnabled: true +``` + ## CI alternatives -The validation scripts can be used in any CI system. +The validation scripts can be used in any CI system. CircleCI example: @@ -135,7 +239,7 @@ version: 2.1 jobs: hrval: docker: - - image: stefanprodan/hrval + - image: stefanprodan/hrval:latest steps: - checkout - run: diff --git a/action.yml b/action.yml index 6867f8d..0b3693a 100644 --- a/action.yml +++ b/action.yml @@ -26,6 +26,9 @@ inputs: awsS3Plugin: description: '(Optional) AWS S3 Plugin to be used in the helm plugin install command' default: '' + helmSourcesCacheEnabled: + description: '(Optional) Enabled Helm source caching, so same release or ref will not be downloaded twice.' + default: 'false' outputs: numFilesTested: description: The number of HelmRelease files which were tested @@ -41,3 +44,4 @@ runs: - ${{ inputs.awsS3Repo }} - ${{ inputs.awsS3RepoName }} - ${{ inputs.awsS3RepoPlugin }} + - ${{ inputs.helmSourcesCacheEnabled }} diff --git a/src/deps.sh b/src/deps.sh index 7885139..26feea2 100755 --- a/src/deps.sh +++ b/src/deps.sh @@ -2,12 +2,12 @@ set -o errexit -curl -sL https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl +curl -sL "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl -curl -sL https://github.com/mikefarah/yq/releases/download/3.1.0/yq_linux_amd64 -o /usr/local/bin/yq && chmod +x /usr/local/bin/yq +curl -sL https://github.com/mikefarah/yq/releases/download/3.4.1/yq_linux_amd64 -o /usr/local/bin/yq && chmod +x /usr/local/bin/yq curl -sSL https://get.helm.sh/helm-v2.16.3-linux-amd64.tar.gz | tar xz && mv linux-amd64/helm /bin/helm && rm -rf linux-amd64 -helm init --client-only --kubeconfig=$HOME/.kube/kubeconfig +helm init --client-only --kubeconfig="${HOME}/.kube/kubeconfig" curl -sSL https://get.helm.sh/helm-v3.1.1-linux-amd64.tar.gz | tar xz && mv linux-amd64/helm /bin/helmv3 && rm -rf linux-amd64 helmv3 version diff --git a/src/hrval-all.sh b/src/hrval-all.sh index b5977b3..eea89b5 100755 --- a/src/hrval-all.sh +++ b/src/hrval-all.sh @@ -9,21 +9,62 @@ HELM_VER=${4-v2} HRVAL="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/hrval.sh" AWS_S3_REPO=${5-false} AWS_S3_REPO_NAME=${6-""} -AWS_S3_PLUGIN={$7-""} +AWS_S3_PLUGIN="${7-""}" +HELM_SOURCES_CACHE_ENABLED=${8-""} + +function configurePrivateChartRepositories() { + + local tempDir + tempDir="$(mktemp -d)" + echo "$HTTP_PRIVATE_CHART_REPOS" > "$tempDir/repositories.json" + local numberOfRepositories + numberOfRepositories=$(yq r "$tempDir/repositories.json" --length repositories) + + for (( i = 0; i < numberOfRepositories; i++ )); do + local url + url=$(yq r "$tempDir/repositories.json" repositories[$i].url) + local username + username=$(yq r "$tempDir/repositories.json" repositories[$i].username) + local password + password=$(yq r "$tempDir/repositories.json" repositories[$i].password) + local repoMD5 + repoMD5=$(/bin/echo "$url" | /usr/bin/md5sum | cut -f1 -d" ") + + >&2 echo "Adding Helm chart repository '$url'" + if [[ ${HELM_VER} == "v3" ]]; then + helmv3 repo add "$repoMD5" "${url}" --username "${username}" --password "${password}" + helmv3 repo update + else + helm repo add "$repoMD5" "${url}" --username "${username}" --password "${password}" + helm repo update + fi + done +} + +if [[ -v HTTP_PRIVATE_CHART_REPOS ]]; then + echo "Configuring Helm chart repositories" + configurePrivateChartRepositories +fi + +if [ "${HELM_SOURCES_CACHE_ENABLED}" == "true" ]; then + CACHEDIR=$(mktemp -d) +else + CACHEDIR="${CACHEDIR}" +fi if [[ ${HELM_VER} == "v2" ]]; then helm init --client-only fi if [[ ${AWS_S3_REPO} == true ]]; then - helm plugin install ${AWS_S3_PLUGIN} - helm repo add ${AWS_S3_REPO_NAME} s3:/${AWS_S3_REPO_NAME}/charts + helm plugin install "${AWS_S3_PLUGIN}" + helm repo add "${AWS_S3_REPO_NAME}" "s3:/${AWS_S3_REPO_NAME}/charts" helm repo update fi # If the path provided is actually a file, just run hrval against this one file if test -f "${DIR}"; then - ${HRVAL} ${DIR} ${IGNORE_VALUES} ${KUBE_VER} ${HELM_VER} + ${HRVAL} "${DIR}" "${IGNORE_VALUES}" "${KUBE_VER}" "${HELM_VER}" "${CACHEDIR}" exit 0 fi @@ -34,7 +75,7 @@ if [ ! -d "$DIR" ]; then fi function isHelmRelease { - KIND=$(yq r ${1} kind) + KIND=$(yq r "${1}" kind) if [[ ${KIND} == "HelmRelease" ]]; then echo true else @@ -43,11 +84,15 @@ function isHelmRelease { } # Find yaml files in directory recursively -DIR_PATH=$(echo ${DIR} | sed "s/^\///;s/\/$//") FILES_TESTED=0 -for f in `find ${DIR} -type f -name '*.yaml' -or -name '*.yml'`; do - if [[ $(isHelmRelease ${f}) == "true" ]]; then - ${HRVAL} ${f} ${IGNORE_VALUES} ${KUBE_VER} ${HELM_VER} +declare -a FOUND_FILES=() +while read -r file; do + FOUND_FILES+=( "$file" ) +done < <(find "${DIR}" -type f -name '*.yaml' -o -name '*.yml') + +for f in "${FOUND_FILES[@]}"; do + if [[ $(isHelmRelease "${f}") == "true" ]]; then + ${HRVAL} "${f}" "${IGNORE_VALUES}" "${KUBE_VER}" "${HELM_VER}" "${CACHEDIR}" FILES_TESTED=$(( FILES_TESTED+1 )) else echo "Ignoring ${f} not a HelmRelease" diff --git a/src/hrval.sh b/src/hrval.sh index f522ce0..4c533bc 100755 --- a/src/hrval.sh +++ b/src/hrval.sh @@ -2,10 +2,11 @@ set -o errexit -HELM_RELEASE=${1} -IGNORE_VALUES=${2} -KUBE_VER=${3-master} -HELM_VER=${4-v2} +HELM_RELEASE="${1}" +IGNORE_VALUES="${2}" +KUBE_VER="${3-master}" +HELM_VER="${4-v2}" +CACHEDIR="${5-""}" if test ! -f "${HELM_RELEASE}"; then echo "\"${HELM_RELEASE}\" Helm release file not found!" @@ -15,94 +16,207 @@ fi echo "Processing ${HELM_RELEASE}" function isHelmRelease { - KIND=$(yq r ${1} kind) - if [[ ${KIND} == "HelmRelease" ]]; then + KIND=$(yq r "${1}" kind) + if [[ "${KIND}" == "HelmRelease" ]]; then echo true else echo false fi } + function download { - CHART_REPO=$(yq r ${1} spec.chart.repository) - CHART_NAME=$(yq r ${1} spec.chart.name) - CHART_VERSION=$(yq r ${1} spec.chart.version) + CHART_REPO="$(yq r "${1}" spec.chart.repository)" + CHART_NAME="$(yq r "${1}" spec.chart.name)" + CHART_VERSION="$(yq r "${1}" spec.chart.version)" CHART_DIR=${2}/${CHART_NAME} - helm repo add ${CHART_NAME} ${CHART_REPO} - helm fetch --version ${CHART_VERSION} --untar ${CHART_NAME}/${CHART_NAME} --untardir ${2} - echo ${CHART_DIR} + + CHART_REPO_MD5=$(/bin/echo "${CHART_REPO}" | /usr/bin/md5sum | cut -f1 -d" ") + + + if [[ ${HELM_VER} == "v3" ]]; then + if [[ $(helmv3 repo list -o yaml | yq r - "[*].name" | grep "$CHART_REPO_MD5") == "$CHART_REPO_MD5" ]]; then + CHART_REPO_ALREADY_ADDED=true + else + CHART_REPO_ALREADY_ADDED=false + fi + else + if [[ $(helm repo list -o yaml | yq r - "[*].Name" | grep "$CHART_REPO_MD5") == "$CHART_REPO_MD5" ]]; then + CHART_REPO_ALREADY_ADDED=true + else + CHART_REPO_ALREADY_ADDED=false + fi + fi + + if [[ "$CHART_REPO_ALREADY_ADDED" = false ]]; then + if [[ "${HELM_VER}" == "v3" ]]; then + helmv3 repo add "${CHART_REPO_MD5}" "${CHART_REPO}" + helmv3 repo update + else + helm repo add "${CHART_REPO_MD5}" "${CHART_REPO}" + helm repo update + fi + fi + + if [[ ${HELM_VER} == "v3" ]]; then + helmv3 fetch --version "${CHART_VERSION}" --untar "${CHART_REPO_MD5}/${CHART_NAME}" --untardir "${2}" + else + helm fetch --version "${CHART_VERSION}" --untar "${CHART_REPO_MD5}/${CHART_NAME}" --untardir "${2}" + fi + + echo "${CHART_DIR}" } + +function fetch { + cd "${1}" + git init -q + git remote add origin "${3}" + git fetch -q origin + git checkout -q "${4}" + cd "${5}" + echo "${2}" +} + + function clone { ORIGIN=$(git rev-parse --show-toplevel) - GIT_REPO=$(yq r ${1} spec.chart.git) + CHART_GIT_REPO=$(yq r "${1}" spec.chart.git) + RELEASE_GIT_REPO=$(git remote get-url origin) + + CHART_BASE_URL=$(echo "${CHART_GIT_REPO}" | sed -e 's/ssh:\/\///' -e 's/http:\/\///' -e 's/https:\/\///' -e 's/git@//' -e 's/:/\//' -e 's/\.git$//') + RELEASE_BASE_URL=$(echo "${RELEASE_GIT_REPO}" | sed -e 's/ssh:\/\///' -e 's/http:\/\///' -e 's/https:\/\///' -e 's/git@//' -e 's/:/\//' -e 's/\.git$//') + if [[ -n "${GITHUB_TOKEN}" ]]; then - BASE_URL=$(echo "${GIT_REPO}" | sed -e 's/ssh:\/\///' -e 's/git@//' -e 's/:/\//') - GIT_REPO="https://${GITHUB_TOKEN}:x-oauth-basic@${BASE_URL}" + CHART_GIT_REPO="https://${GITHUB_TOKEN}:x-oauth-basic@${CHART_BASE_URL}" elif [[ -n "${GITLAB_CI_TOKEN}" ]]; then - BASE_URL=$(echo "${GIT_REPO}" | sed -e 's/ssh:\/\///' -e 's/git@//' -e 's/:/\//') - GIT_REPO="https://gitlab-ci-token:${GITLAB_CI_TOKEN}@${BASE_URL}" + CHART_GIT_REPO="https://gitlab-ci-token:${GITLAB_CI_TOKEN}@${CHART_BASE_URL}" + fi + + GIT_REF=$(yq r "${1}" spec.chart.ref) + CHART_PATH=$(yq r "${1}" spec.chart.path) + + if [ -n "${3}" ]; then + if [[ "${CHART_BASE_URL}" == "${RELEASE_BASE_URL}" ]] && [[ "${GIT_REF}" == "${4}" ]]; then + # Clone from the head repository branch/ref + fetch "${2}" "${2}/${CHART_PATH}" "${RELEASE_GIT_REPO}" "${3}" "${ORIGIN}" + else + # Regular clone + fetch "${2}" "${2}/${CHART_PATH}" "${CHART_GIT_REPO}" "${GIT_REF}" "${ORIGIN}" + fi + else + fetch "${2}" "${2}/${CHART_PATH}" "${CHART_GIT_REPO}" "${GIT_REF}" "${ORIGIN}" fi - GIT_REF=$(yq r ${1} spec.chart.ref) - CHART_PATH=$(yq r ${1} spec.chart.path) - cd ${2} - git init -q - git remote add origin ${GIT_REPO} - git fetch -q origin - git checkout -q ${GIT_REF} - cd ${ORIGIN} - echo ${2}/${CHART_PATH} } + +function retrieve_sources { + HELM_RELEASE="${1}" + TMPDIR="${2}" + + CHART_PATH=$(yq r "${HELM_RELEASE}" spec.chart.path) + + if [[ -z "${CACHEDIR}" ]]; then + + # Retrieve files directly into tempdir + if [[ -z "${CHART_PATH}" ]]; then + >&2 echo "Downloading to ${TMPDIR}" + CHART_DIR=$(download "${HELM_RELEASE}" "${TMPDIR}" "${HELM_VER}" | tail -n1) + else + >&2 echo "Cloning to ${TMPDIR}" + CHART_DIR=$(clone "${HELM_RELEASE}" "${TMPDIR}" "${HRVAL_HEAD_BRANCH}" "${HRVAL_BASE_BRANCH}" | tail -n1) + fi + + else + # Retrieve existing helm chart source from cache, + # or create new cache directory if it does not exist yet. + + if [[ -z "${CHART_PATH}" ]]; then + # Caches releases from Helm repos + + CHART_REPO=$(yq r "${HELM_RELEASE}" spec.chart.repository) + CHART_REPO_MD5=$(/bin/echo "${CHART_REPO}" | /usr/bin/md5sum | cut -f1 -d" ") + CHART_NAME=$(yq r "${HELM_RELEASE}" spec.chart.name) + CHART_VERSION=$(yq r "${HELM_RELEASE}" spec.chart.version) + CHART_LOCAL_PATH="${CACHEDIR}/${CHART_REPO_MD5}/${CHART_NAME}/${CHART_VERSION}" + + if [[ ! -d ${CHART_LOCAL_PATH} ]]; then + mkdir -p "${CHART_LOCAL_PATH}" + >&2 echo "Downloading to ${CHART_LOCAL_PATH}" + CHART_DIR=$(download "${HELM_RELEASE}" "${CHART_LOCAL_PATH}" "${HELM_VER}" | tail -n1) + else + >&2 echo "Using cached sources from ${CHART_LOCAL_PATH}" + CHART_DIR="${CHART_LOCAL_PATH}/${CHART_NAME}" + fi + + else + # Caches releases from Git repos + + CHART_GIT_REPO=$(yq r "${1}" spec.chart.git) + CHART_PATH=$(yq r "${1}" spec.chart.path) + GIT_REF=$(yq r "${1}" spec.chart.ref) + + CHART_LOCAL_PATH="${CACHEDIR}/${CHART_GIT_REPO}/${GIT_REF}" + + if [[ ! -d "${CHART_LOCAL_PATH}" ]]; then + mkdir -p "${CHART_LOCAL_PATH}" + >&2 echo "Cloning to ${CHART_LOCAL_PATH}" + CHART_DIR=$(clone "${HELM_RELEASE}" "${CHART_LOCAL_PATH}" "${HRVAL_HEAD_BRANCH}" "${HRVAL_BASE_BRANCH}" | tail -n1) + else + >&2 echo "Using cached sources from ${CHART_LOCAL_PATH}" + CHART_DIR="${CHART_LOCAL_PATH}/${CHART_PATH}" + fi + + fi + + fi + + echo "${CHART_DIR}" +} + + function validate { - if [[ $(isHelmRelease ${HELM_RELEASE}) == "false" ]]; then + if [[ $(isHelmRelease "${HELM_RELEASE}") == "false" ]]; then echo "\"${HELM_RELEASE}\" is not of kind HelmRelease!" exit 1 fi - TMPDIR=$(mktemp -d) - CHART_PATH=$(yq r ${HELM_RELEASE} spec.chart.path) - - if [[ -z "${CHART_PATH}" ]]; then - echo "Downloading to ${TMPDIR}" - CHART_DIR=$(download ${HELM_RELEASE} ${TMPDIR}| tail -n1) - else - echo "Cloning to ${TMPDIR}" - CHART_DIR=$(clone ${HELM_RELEASE} ${TMPDIR}| tail -n1) - fi + TMPDIR="$(mktemp -d)" + CHART_DIR=$(retrieve_sources "${HELM_RELEASE}" "${TMPDIR}") + CHART_PATH=$(yq r "${HELM_RELEASE}" spec.chart.path) - HELM_RELEASE_NAME=$(yq r ${HELM_RELEASE} metadata.name) - HELM_RELEASE_NAMESPACE=$(yq r ${HELM_RELEASE} metadata.namespace) + HELM_RELEASE_NAME=$(yq r "${HELM_RELEASE}" metadata.name) + HELM_RELEASE_NAMESPACE=$(yq r "${HELM_RELEASE}" metadata.namespace) - if [[ ${IGNORE_VALUES} == "true" ]]; then - echo "Ingnoring Helm release values" - echo "" > ${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml + if [[ "${IGNORE_VALUES}" == "true" ]]; then + echo "Ignoring Helm release values" + echo "" > "${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml" else echo "Extracting values to ${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml" - yq r ${HELM_RELEASE} spec.values > ${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml + yq r -X "${HELM_RELEASE}" spec.values > "${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml" fi echo "Writing Helm release to ${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml" if [[ ${HELM_VER} == "v3" ]]; then if [[ "${CHART_PATH}" ]]; then - helmv3 dependency build ${CHART_DIR} + helmv3 dependency build "${CHART_DIR}" fi - helmv3 template ${HELM_RELEASE_NAME} ${CHART_DIR} \ - --namespace ${HELM_RELEASE_NAMESPACE} \ + helmv3 template "${HELM_RELEASE_NAME}" "${CHART_DIR}" \ + --namespace "${HELM_RELEASE_NAMESPACE}" \ --skip-crds=true \ - -f ${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml > ${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml + -f "${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml" > "${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml" else if [[ "${CHART_PATH}" ]]; then - helm dependency build ${CHART_DIR} + helm dependency build "${CHART_DIR}" fi - helm template ${CHART_DIR} \ - --name ${HELM_RELEASE_NAME} \ - --namespace ${HELM_RELEASE_NAMESPACE} \ - -f ${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml > ${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml + helm template "${CHART_DIR}" \ + --name "${HELM_RELEASE_NAME}" \ + --namespace "${HELM_RELEASE_NAMESPACE}" \ + -f "${TMPDIR}/${HELM_RELEASE_NAME}.values.yaml" > "${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml" fi echo "Validating Helm release ${HELM_RELEASE_NAME}.${HELM_RELEASE_NAMESPACE} against Kubernetes ${KUBE_VER}" - kubeval --strict --ignore-missing-schemas --kubernetes-version ${KUBE_VER} ${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml + kubeval --strict --ignore-missing-schemas --kubernetes-version "${KUBE_VER}" "${TMPDIR}/${HELM_RELEASE_NAME}.release.yaml" } validate