From 7c6ad06d3d12f3560e5c02e90477bb87b3089a50 Mon Sep 17 00:00:00 2001 From: James Munnelly Date: Wed, 4 Jul 2018 14:08:50 +0100 Subject: [PATCH 1/3] Use kubernetes-helm/chart-testing scripts to verify helm chart --- hack/verify-chart-version.sh | 100 +------ test/chart/.testenv | 25 ++ test/chart/chart_test.sh | 165 +++++++++++ test/chart/etc/chart_schema.yaml | 20 ++ test/chart/etc/lintconf.yaml | 42 +++ test/chart/lib/chartlib.sh | 477 +++++++++++++++++++++++++++++++ 6 files changed, 738 insertions(+), 91 deletions(-) create mode 100644 test/chart/.testenv create mode 100755 test/chart/chart_test.sh create mode 100644 test/chart/etc/chart_schema.yaml create mode 100644 test/chart/etc/lintconf.yaml create mode 100644 test/chart/lib/chartlib.sh diff --git a/hack/verify-chart-version.sh b/hack/verify-chart-version.sh index e6491006f..c67b638f4 100755 --- a/hack/verify-chart-version.sh +++ b/hack/verify-chart-version.sh @@ -1,100 +1,18 @@ -#!/bin/bash -# Copyright 2017 The Kubernetes Authors All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail -semvercompareOldVer="" -semvercompareNewVer="" +readonly REPO_ROOT=$(git rev-parse --show-toplevel) -if [ -z "${PULL_BASE_SHA+a}" ]; then - echo "PULL_BASE_SHA must be set" +if [ -z "${PULL_BASE_REF:-}" ]; then + echo "PULL_BASE_REF must be set to a target branch name" exit 1 fi -if ! git remote get-url jetstack; then - git remote add jetstack https://github.com/jetstack/cert-manager -fi - -git fetch jetstack "${PULL_BASE_SHA}:refs/remotes/jetstack/pull-base" - -SCRIPT_ROOT="$(dirname "${BASH_SOURCE}")/.." - -CHANGED_FOLDERS=`git diff --find-renames --name-only $(git merge-base jetstack/pull-base HEAD) "${SCRIPT_ROOT}/contrib/charts/" | awk -F/ '{print $1"/"$2"/"$3}' | uniq` - -# Verify that the semver for the chart was increased -semvercompare() { - set +e - printf "\nChecking the Chart version has increased for the chart at ${1}\n" - - # Checkout the Chart.yaml file on master to read the version for comparison - # Sending the output to a file and the error to /dev/null so that these - # messages do not clutter up the end user output - $(git show jetstack/pull-base:$1/Chart.yaml 1> /tmp/Chart.yaml 2> /dev/null) - - ## If the chart is new git cannot checkout the chart. In that case return - if [ $? -ne 0 ]; then - echo "Unable to find Chart on master. New chart detected." - return - fi - - semvercompareOldVer=`yaml r /tmp/Chart.yaml version` - semvercompareNewVer=`yaml r $1/Chart.yaml version` - - # Pre-releases may not be API compatible. So, when tools compare versions - # they often skip pre-releases. vert can force looking at pre-releases by - # adding a dash on the end followed by pre-release. -0 on the end will force - # looking for all valid pre-releases since a prerelease cannot start with a 0. - # For example, 1.2.3-0 will include looking for pre-releases. - local ret - local out - if [[ $semvercompareOldVer == *"-"* ]]; then # Found the - to denote it has a pre-release - out=$(vert ">$semvercompareOldVer" $semvercompareNewVer) - ret=$? - else - # No pre-release was found so we increment the patch version and attach a - # -0 to enable pre-releases being found. - local ov=( ${semvercompareOldVer//./ } ) # Turn the version into an array - ((ov[2]+=1)) # Increment the patch release - out=$(vert ">${ov[0]}.${ov[1]}.${ov[2]}-0" $semvercompareNewVer) - ret=$? - fi - - if [ $ret -ne 0 ]; then - echo "Error please increment the new chart version to be greater than the existing version of $semvercompareOldVer" - exitCode=1 - else - echo "New higher version $semvercompareNewVer found" - fi - - # Clean up - rm /tmp/Chart.yaml -} - -exitCode=0 - -for directory in ${CHANGED_FOLDERS}; do - if [ "${directory}" == "contrib/charts" ]; then - continue - fi - if [ ! -d "${directory}" ]; then - echo "Directory ${directory} has been deleted. Skipping version check..." - continue - fi - semvercompare "${directory}" -done - -exit "${exitCode}" +docker run --rm -v "${REPO_ROOT}:/workdir" --workdir /workdir -e TARGET_BRANCH="${PULL_BASE_REF}" \ + gcr.io/kubernetes-charts-ci/chart-testing:v1.0.2 \ + /workdir/test/chart/chart_test.sh \ + --no-install \ + --config test/chart/.testenv diff --git a/test/chart/.testenv b/test/chart/.testenv new file mode 100644 index 000000000..0a50327c1 --- /dev/null +++ b/test/chart/.testenv @@ -0,0 +1,25 @@ +# The name of the Git remote +# Omitted to ensure it is provided on the CLI +# REMOTE=upstream + +# The name of the Git target branch +# Omitted to ensure it is provided on the CLI +# TARGET_BRANCH=master + +# Chart directories separated by a space +CHART_DIRS=( + contrib/charts +) + +# Charts that should be skipped +EXCLUDED_CHARTS=( + pebble + vault +) + +# Additional chart repos to add (=), separated by a space +CHART_REPOS=( + incubator=https://kubernetes-charts-incubator.storage.googleapis.com/ +) + +TIMEOUT=600 diff --git a/test/chart/chart_test.sh b/test/chart/chart_test.sh new file mode 100755 index 000000000..f5841fef3 --- /dev/null +++ b/test/chart/chart_test.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash + +## This script has been taken from https://github.com/kubernetes-helm/chart-testing +## It has the same dependencies as described in that repo, and should ideally be run +## within the docker image published by that same repository in order to make sure +## the correct dependencies are included. +## +## Run from within the root of the repository with: +## +## docker run --rm -v "$(pwd):/workdir" --workdir /workdir \ +## gcr.io/kubernetes-charts-ci/chart-testing:v1.0.2 \ +## /workdir/test/chart_test.sh \ +## --no-install \ +## --config test/.testenv + +# Copyright 2018 The Helm Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset +set -o pipefail + +readonly REPO_ROOT=$(git rev-parse --show-toplevel) +readonly SCRIPT_DIR=$(dirname "$(readlink -f "$0")") + +show_help() { +cat << EOF +Usage: $(basename "$0") + Lint, install, and test Helm charts. + -h, --help Display help + --verbose Display verbose output + --no-lint Skip chart linting + --no-install Skip chart installation + --config Path to the config file (optional) + -- End of all options +EOF +} + +main() { + local no_lint= + local no_install= + local config= + local verbose= + + while :; do + case "${1:-}" in + -h|--help) + show_help + exit + ;; + --verbose) + verbose=true + ;; + --no-install) + no_install=true + ;; + --no-lint) + no_lint=true + ;; + --config) + if [ -n "$2" ]; then + config="$2" + shift + else + echo "ERROR: '--config' cannot be empty." >&2 + exit 1 + fi + ;; + -?*) + echo "WARN: Unknown option (ignored): $1" >&2 + ;; + *) + break + ;; + esac + + shift + done + + if [[ -n "$config" ]]; then + if [[ -f "$config" ]]; then + # shellcheck disable=SC1090 + source "$config" + else + echo "ERROR: Specified config file does not exist: $config" >&2 + exit 1 + fi + fi + + # shellcheck source=lib/chartlib.sh + source "$SCRIPT_DIR/lib/chartlib.sh" + + [[ -n "$verbose" ]] && set -o xtrace + + pushd "$REPO_ROOT" > /dev/null + + local exit_code=0 + + read -ra changed_dirs <<< "$(chartlib::detect_changed_directories)" + + if [[ -n "${changed_dirs[*]}" ]]; then + echo "Charts to be installed and tested: ${changed_dirs[*]}" + + chartlib::init_helm + + local summary=() + + for chart_dir in "${changed_dirs[@]}"; do + echo '' + echo '--------------------------------------------------------------------------------' + echo " Processing chart '$chart_dir'..." + echo '--------------------------------------------------------------------------------' + echo '' + + local error= + + if [[ -z "$no_lint" ]]; then + if ! chartlib::validate_chart "$chart_dir"; then + error=true + fi + if ! chartlib::lint_chart_with_all_configs "$chart_dir"; then + error=true + fi + fi + + if [[ -z "$no_install" && -z "$error" ]]; then + if ! chartlib::install_chart_with_all_configs "$chart_dir"; then + error=true + fi + fi + + if [[ -z "$error" ]]; then + summary+=(" ✔︎ $chart_dir") + else + summary+=(" ✖︎ $chart_dir") + exit_code=1 + fi + done + else + summary+=('No chart changes detected.') + fi + + echo '--------------------------------------------------------------------------------' + for line in "${summary[@]}"; do + echo "$line" + done + echo '--------------------------------------------------------------------------------' + + popd > /dev/null + + exit "$exit_code" +} + +main "$@" \ No newline at end of file diff --git a/test/chart/etc/chart_schema.yaml b/test/chart/etc/chart_schema.yaml new file mode 100644 index 000000000..e75022365 --- /dev/null +++ b/test/chart/etc/chart_schema.yaml @@ -0,0 +1,20 @@ +name: str() +home: str() +version: str() +appVersion: any(str(), num()) +description: str() +keywords: list(str(), required=False) +sources: list(str(), required=False) +maintainers: list(include('maintainer'), required=False) +icon: str(required=False) +engine: str(required=False) +condition: str(required=False) +tags: str(required=False) +deprecated: bool(required=False) +kubeVersion: str(required=False) +annotations: map(str(), str(), required=False) +--- +maintainer: + name: str() + email: str(required=False) + url: str(required=False) diff --git a/test/chart/etc/lintconf.yaml b/test/chart/etc/lintconf.yaml new file mode 100644 index 000000000..90f48c889 --- /dev/null +++ b/test/chart/etc/lintconf.yaml @@ -0,0 +1,42 @@ +--- +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 2 + document-end: disable + document-start: disable # No --- to start a file + empty-lines: + max: 2 + max-start: 0 + max-end: 0 + hyphens: + max-spaces-after: 1 + indentation: + spaces: consistent + indent-sequences: whatever # - list indentation will handle both indentation and without + check-multi-line-strings: false + key-duplicates: enable + line-length: disable # Lines can be any length + new-line-at-end-of-file: enable + new-lines: + type: unix + trailing-spaces: enable + truthy: + level: warning diff --git a/test/chart/lib/chartlib.sh b/test/chart/lib/chartlib.sh new file mode 100644 index 000000000..62226914d --- /dev/null +++ b/test/chart/lib/chartlib.sh @@ -0,0 +1,477 @@ +#!/usr/bin/env bash + +# Copyright 2018 The Helm Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset +set -o pipefail +shopt -s nullglob + + +readonly REMOTE="${REMOTE:-origin}" +readonly TARGET_BRANCH="${TARGET_BRANCH:-master}" +readonly TIMEOUT="${TIMEOUT:-300}" +readonly LINT_CONF="${LINT_CONF:-/testing/etc/lintconf.yaml}" +readonly CHART_YAML_SCHEMA="${CHART_YAML_SCHEMA:-/testing/etc/chart_schema.yaml}" +readonly VALIDATE_MAINTAINERS="${VALIDATE_MAINTAINERS:-true}" + +# Special handling for arrays +[[ -z "${CHART_DIRS[*]}" ]] && CHART_DIRS=(charts); readonly CHART_DIRS +[[ -z "${EXCLUDED_CHARTS[*]}" ]] && EXCLUDED_CHARTS=(); readonly EXCLUDED_CHARTS +[[ -z "${CHART_REPOS[*]}" ]] && CHART_REPOS=(); readonly CHART_REPOS + +echo +echo '--------------------------------------------------------------------------------' +echo ' Environment:' +echo " REMOTE=$REMOTE" +echo " TARGET_BRANCH=$TARGET_BRANCH" +echo " CHART_DIRS=${CHART_DIRS[*]}" +echo " EXCLUDED_CHARTS=${EXCLUDED_CHARTS[*]}" +echo " CHART_REPOS=${CHART_REPOS[*]}" +echo " TIMEOUT=$TIMEOUT" +echo " LINT_CONF=$LINT_CONF" +echo " CHART_YAML_SCHEMA=$CHART_YAML_SCHEMA" +echo " VALIDATE_MAINTAINERS=$VALIDATE_MAINTAINERS" +echo '--------------------------------------------------------------------------------' +echo + + +# Detects chart directories that have changes against the +# target branch ("$REMOTE/$TARGET_BRANCH"). +chartlib::detect_changed_directories() { + local merge_base + merge_base="$(git merge-base "$REMOTE/$TARGET_BRANCH" HEAD)" + + local changed_dirs=() + local dir + + while read -r dir; do + local excluded= + for excluded_dir in "${EXCLUDED_CHARTS[@]}"; do + if [[ "$dir" == "$excluded_dir" ]]; then + excluded=true + break + fi + done + if [[ -z "$excluded" && -d "$dir" ]]; then + changed_dirs=("${changed_dirs[@]}" "$dir") + fi + + ## @munnerz: because the cert-manager repository stores charts in the contrib/ + ## subdirectory, we must modify the below line from $1/$2 to be $1/$2/$3. + ## In future, we should PR upstream so we no longer hardcode the depth of + ## directories required for this script. + done < <(git diff --find-renames --name-only "$merge_base" "${CHART_DIRS[@]}" | awk -F/ '{ print $1"/"$2"/"$3 }' | uniq) + + echo "${changed_dirs[@]}" +} + +# Initializes the Helm client and add configured repos. +chartlib::init_helm() { + echo 'Initializing Helm client...' + + helm init --client-only + + for repo in "${CHART_REPOS[@]}"; do + local name="${repo%=*}" + local url="${repo#*=}" + + helm repo add "$name" "$url" + done +} + +# Checks a chart for a version bump comparing the version from Chart.yaml +# with that from the target branch. +# Args: +# $1 The chart directory +chartlib::check_for_version_bump() { + local chart_dir="${1?Chart directory is required}" + + echo "Checking chart '$chart_dir' for a version bump..." + + # Check if chart exists on taget branch + if ! git cat-file -e "$REMOTE/$TARGET_BRANCH:$chart_dir/Chart.yaml" > /dev/null 2>&1; then + echo "Unable to find chart on master. New chart detected." + return 0 + fi + + # Compare version of chart under test with that on the target branch + + local old_version + old_version=$(yq -r .version <(git show "$REMOTE/$TARGET_BRANCH:$chart_dir/Chart.yaml")) + echo "Chart version on" "$REMOTE/$TARGET_BRANCH" ":" "$old_version" + + local new_version + new_version=$(yq -r .version "$chart_dir/Chart.yaml") + echo "New chart version: " "$new_version" + + # Pre-releases may not be API compatible. So, when tools compare versions + # they often skip pre-releases. vert can force looking at pre-releases by + # adding a dash on the end followed by pre-release. -0 on the end will force + # looking for all valid pre-releases since a pre-release cannot start with a 0. + # For example, 1.2.3-0 will include looking for pre-releases. + if [[ $old_version == *-* ]]; then # Found the - to denote it has a pre-release + if vert ">$old_version" "$new_version"; then + echo "Chart version ok. Version bumped." + return 0 + fi + else + # No pre-release was found so we increment the patch version and attach a + # -0 to enable pre-releases being found. + local old_version_array + read -ra old_version_array <<< "${old_version//./ }" # Turn the version into an array + + (( old_version_array[2] += 1 )) # Increment the patch release + if vert ">${old_version_array[0]}.${old_version_array[1]}.${old_version_array[2]}-0" "$new_version"; then + echo "Chart version ok. Version bumped." + return 0 + fi + fi + + chartlib::error "Chart version not ok. Needs a version bump." + return 1 +} + +# Validates the Chart.yaml against a YAML schema. +# Args: +# $1 The chart directory +chartlib::validate_chart_yaml() { + local chart_dir="${1?Chart directory is required}" + + echo "Validating Chart.yaml" + yamale --schema "$CHART_YAML_SCHEMA" "$chart_dir/Chart.yaml" +} + +# Validates maintainer names in Chart.yaml to be valid Github users. +# Args: +# $1 The chart directory +chartlib::validate_maintainers() { + local chart_dir="${1?Chart directory is required}" + + echo "Validating maintainers" + + # We require maintainers for non-deprecated charts + local deprecated + deprecated=$(yq -r '.deprecated // empty' "$chart_dir/Chart.yaml") + + local maintainers + maintainers=$(yq -r '.maintainers // empty' "$chart_dir/Chart.yaml") + + if [[ -n "$deprecated" ]]; then + if [[ -n "$maintainers" ]]; then + chartlib::error "Deprecated charts must not have any maintainers in 'Chart.yaml'." + return 1 + else + return 0 + fi + else + if [[ -z "$maintainers" ]]; then + echo "No maintainers found in 'Chart.yaml'." + fi + fi + + while read -r name; do + echo "Verifying maintainer '$name'..." + if [[ $(curl --silent --output /dev/null --write-out "%{http_code}" --fail --head "https://github.com/$name") -ne 200 ]]; then + chartlib::error "'$name' is not a valid GitHub account. Please use a valid Github account to help us communicate with maintainers in PRs/issues." + return 1 + fi + done < <(yq -r '.maintainers[].name' "$chart_dir/Chart.yaml") +} + +# Lints a YAML file. +# Args: +# $1 The YAML file to lint +chartlib::lint_yaml_file() { + local file="${1?Specify YAML file for linting}" + + echo "Linting '$file'..." + + if [[ -f "$file" ]]; then + yamllint --config-file "$LINT_CONF" "$file" + else + chartlib::error "File '$file' does not exist." + return 1 + fi +} + +# Validates a chart: +# - Checks for a version bump +# - Lints Chart.yaml and values.yaml +# - Validates Chart.yaml against schema +# - Validates maintainers +# Args: +# $1 The chart directory +chartlib::validate_chart() { + local chart_dir="${1?Chart directory is required}" + local error= + + echo "Validating chart '$chart_dir'..." + + chartlib::check_for_version_bump "$chart_dir" || error=true + chartlib::lint_yaml_file "$chart_dir/Chart.yaml" || error=true + chartlib::lint_yaml_file "$chart_dir/values.yaml" || error=true + chartlib::validate_chart_yaml "$chart_dir" || error=true + + if [[ "$VALIDATE_MAINTAINERS" == true ]]; then + chartlib::validate_maintainers "$chart_dir" || error=true + fi + + if [[ -n "$error" ]]; then + chartlib::error 'Chart validation failed.' + return 1 + fi +} + +# Lints a chart. +# Args: +# $1 The chart directory +# $2 A custom values file for the chart installation (optional) +chartlib::lint_chart_with_single_config() { + local chart_dir="${1?Chart directory is required}" + local values_file="${2:-}" + + echo "Building dependencies for chart '$chart_dir'..." + helm dependency build "$chart_dir" + + if [[ -n "$values_file" ]]; then + echo "Using custom values file '$values_file'..." + + echo "Linting chart '$chart_dir'..." + helm lint "$chart_dir" --values "$values_file" + else + echo "Chart does not provide test values. Using defaults..." + + echo "Linting chart '$chart_dir'..." + helm lint "$chart_dir" + fi +} + +# Installs and tests a chart. The release and the namespace are +# automatically deleted afterwards. +# Args: +# $1 The chart directory +# $2 The release name for the chart to be installed +# $3 The namespace to install the chart in +# $4 A custom values file for the chart installation (optional) +chartlib::install_chart_with_single_config() { + local chart_dir="${1?Chart directory is required}" + local release="${2?Release is required}" + local namespace="${3?Namespace is required}" + local values_file="${4:-}" + + # Capture subshell output + exec 3>&1 + + if ! ( + set -o errexit + + # Run in subshell so we can use a trap within the function. + trap 'chartlib::print_pod_details_and_logs "$namespace" || true; chartlib::delete_release "$release" || true; chartlib::delete_namespace "$namespace" || true' EXIT + + echo "Building dependencies for chart '$chart_dir'..." + helm dependency build "$chart_dir" + + echo "Installing chart '$chart_dir' into namespace '$namespace'..." + + if [[ -n "$values_file" ]]; then + echo "Using custom values file '$values_file'..." + helm install "$chart_dir" --name "$release" --namespace "$namespace" --wait --timeout "$TIMEOUT" --values "$values_file" + else + echo "Chart does not provide test values. Using defaults..." + helm install "$chart_dir" --name "$release" --namespace "$namespace" --wait --timeout "$TIMEOUT" + fi + + # For deployments --wait may not be sufficient because it looks at 'maxUnavailable' which is 0 by default. + for deployment in $(kubectl get deployment --namespace "$namespace" --output jsonpath='{.items[*].metadata.name}'); do + kubectl rollout status "deployment/$deployment" --namespace "$namespace" + done + + echo "Testing chart '$chart_dir' in namespace '$namespace'..." + helm test "$release" --cleanup --timeout "$TIMEOUT" + + ) >&3; then + + chartlib::error "Chart installation failed: $chart_dir" + return 1 + fi +} + +# Lints a chart for all custom values files matching '*.values.yaml' +# in the 'ci' subdirectory. +# Args: +# $1 The chart directory +chartlib::lint_chart_with_all_configs() { + local chart_dir="${1?Chart directory is required}" + + local has_test_values= + for values_file in "$chart_dir"/ci/*-values.yaml; do + has_test_values=true + chartlib::lint_chart_with_single_config "$chart_dir" "$values_file" + done + + if [[ -z "$has_test_values" ]]; then + chartlib::lint_chart_with_single_config "$chart_dir" + fi +} + +# Installs a chart for all custom values files matching '*.values.yaml' +# in the 'ci' subdirectory. If no custom values files are found, the chart +# is installed with defaults. If $BUILD_ID is set, it is used as +# name for the namespace to install the chart in. Otherwise, the chart +# name is taken as the namespace name. Namespace and release are suffixed with +# an index. Releases and namespaces are automatically deleted afterwards. +# Args: +# $1 The chart directory +chartlib::install_chart_with_all_configs() { + local chart_dir="${1?Chart directory is required}" + local index=0 + + local release + release=$(yq -r .name < "$chart_dir/Chart.yaml") + + local random_suffix + random_suffix=$(tr -dc a-z0-9 < /dev/urandom | fold -w 16 | head -n 1) + + local namespace="${BUILD_ID:-"$release"}-$random_suffix" + local release="$release-$random_suffix" + + local has_test_values= + for values_file in "$chart_dir"/ci/*-values.yaml; do + has_test_values=true + chartlib::install_chart_with_single_config "$chart_dir" "$release-$index" "$namespace-$index" "$values_file" + ((index += 1)) + done + + if [[ -z "$has_test_values" ]]; then + chartlib::install_chart_with_single_config "$chart_dir" "$release" "$namespace" + fi +} + +# Prints log for all pods in the specified namespace. +# Args: +# $1 The namespace +chartlib::print_pod_details_and_logs() { + local namespace="${1?Namespace is required}" + + kubectl get pods --show-all --no-headers --namespace "$namespace" | awk '{ print $1 }' | while read -r pod; do + if [[ -n "$pod" ]]; then + printf '\n================================================================================\n' + printf ' Details from pod %s\n' "$pod" + printf '================================================================================\n' + + printf '\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n' + printf ' Description of pod %s\n' "$pod" + printf '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n' + + kubectl describe pod --namespace "$namespace" "$pod" || true + + printf '\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n' + printf ' End of description for pod %s\n' "$pod" + printf '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n' + + local init_containers + init_containers=$(kubectl get pods --show-all --output jsonpath="{.spec.initContainers[*].name}" --namespace "$namespace" "$pod") + for container in $init_containers; do + printf -- '\n--------------------------------------------------------------------------------\n' + printf ' Logs of init container %s in pod %s\n' "$container" "$pod" + printf -- '--------------------------------------------------------------------------------\n\n' + + kubectl logs --namespace "$namespace" --container "$container" "$pod" || true + + printf -- '\n--------------------------------------------------------------------------------\n' + printf ' End of logs of init container %s in pod %s\n' "$container" "$pod" + printf -- '--------------------------------------------------------------------------------\n' + done + + local containers + containers=$(kubectl get pods --show-all --output jsonpath="{.spec.containers[*].name}" --namespace "$namespace" "$pod") + for container in $containers; do + printf '\n--------------------------------------------------------------------------------\n' + printf -- ' Logs of container %s in pod %s\n' "$container" "$pod" + printf -- '--------------------------------------------------------------------------------\n\n' + + kubectl logs --namespace "$namespace" --container "$container" "$pod" || true + + printf -- '\n--------------------------------------------------------------------------------\n' + printf ' End of logs of container %s in pod %s\n' "$container" "$pod" + printf -- '--------------------------------------------------------------------------------\n' + done + + printf '\n================================================================================\n' + printf ' End of details for pod %s\n' "$pod" + printf '================================================================================\n\n' + fi + done +} + +# Deletes a release. +# Args: +# $1 The name of the release to delete +chartlib::delete_release() { + local release="${1?Release is required}" + + echo "Deleting release '$release'..." + helm delete --purge "$release" --timeout "$TIMEOUT" +} + +# Deletes a namespace. +# Args: +# $1 The namespace to delete +chartlib::delete_namespace() { + local namespace="${1?Namespace is required}" + + echo "Deleting namespace '$namespace'..." + kubectl delete namespace "$namespace" + + echo -n "Waiting for namespace '$namespace' to terminate..." + + local max_retries=30 + local retry=0 + local sleep_time_sec=3 + while ((retry < max_retries)); do + sleep "$sleep_time_sec" + ((retry++)) + + if ! kubectl get namespace "$namespace" &> /dev/null; then + echo + echo "Namespace '$namespace' terminated." + return 0 + fi + + echo -n '.' + done + + echo + + chartlib::error "Namespace '$namespace' not terminated after $((max_retries * sleep_time_sec)) s." + + echo "Force-deleting pods..." + kubectl delete pods --namespace "$namespace" --all --force --grace-period 0 || true + + sleep 3 + + if ! kubectl get namespace "$namespace" &> /dev/null; then + echo "Force-deleting namespace '$namespace'..." + kubectl delete namespace "$namespace" --ignore-not-found --force --grace-period 0 || true + fi +} + +# Logs an error. +# Args: +# $1 The error message +chartlib::error() { + printf '\e[31mERROR: %s\n\e[39m' "$1" >&2 +} \ No newline at end of file From a7a50cc26c2fe102e00cbdb49202d0d9ab25724b Mon Sep 17 00:00:00 2001 From: James Munnelly Date: Wed, 4 Jul 2018 14:33:38 +0100 Subject: [PATCH 2/3] Default and set REMOTE during chart test --- hack/verify-chart-version.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hack/verify-chart-version.sh b/hack/verify-chart-version.sh index c67b638f4..75cd44aa2 100755 --- a/hack/verify-chart-version.sh +++ b/hack/verify-chart-version.sh @@ -5,12 +5,23 @@ set -o nounset set -o pipefail readonly REPO_ROOT=$(git rev-parse --show-toplevel) +readonly UPSTREAM_REPO="https://github.com/jetstack/cert-manager.git" if [ -z "${PULL_BASE_REF:-}" ]; then echo "PULL_BASE_REF must be set to a target branch name" exit 1 fi +if [ -z "${REMOTE:-}" ]; then + echo "+++ REMOTE not set - defaulting to 'upstream'" + export REMOTE="upstream" +fi + +if [ ! git remote get-url "${REMOTE}" 2>&1 /dev/null ]; then + echo "+++ Remote '${REMOTE}' does not exist. Setting to " + git remote add "${REMOTE}" "${UPSTREAM_REPO}" +fi + docker run --rm -v "${REPO_ROOT}:/workdir" --workdir /workdir -e TARGET_BRANCH="${PULL_BASE_REF}" \ gcr.io/kubernetes-charts-ci/chart-testing:v1.0.2 \ /workdir/test/chart/chart_test.sh \ From 55f4f279d1477749689e70e77cae41ebf8ccf87d Mon Sep 17 00:00:00 2001 From: James Munnelly Date: Wed, 4 Jul 2018 14:41:57 +0100 Subject: [PATCH 3/3] Fix remote handling --- hack/verify-chart-version.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hack/verify-chart-version.sh b/hack/verify-chart-version.sh index 75cd44aa2..b5f2b5fed 100755 --- a/hack/verify-chart-version.sh +++ b/hack/verify-chart-version.sh @@ -17,12 +17,16 @@ if [ -z "${REMOTE:-}" ]; then export REMOTE="upstream" fi -if [ ! git remote get-url "${REMOTE}" 2>&1 /dev/null ]; then - echo "+++ Remote '${REMOTE}' does not exist. Setting to " +if ! git remote get-url "${REMOTE}" > /dev/null 2>&1; then + echo "+++ Remote '${REMOTE}' does not exist. Setting to ${UPSTREAM_REPO}" git remote add "${REMOTE}" "${UPSTREAM_REPO}" fi -docker run --rm -v "${REPO_ROOT}:/workdir" --workdir /workdir -e TARGET_BRANCH="${PULL_BASE_REF}" \ +git fetch "${REMOTE}" + +docker run --rm -v "${REPO_ROOT}:/workdir" --workdir /workdir \ + -e REMOTE="${REMOTE}" \ + -e TARGET_BRANCH="${PULL_BASE_REF}" \ gcr.io/kubernetes-charts-ci/chart-testing:v1.0.2 \ /workdir/test/chart/chart_test.sh \ --no-install \