From 958d783919799e5851754b244d8094b9b7c82054 Mon Sep 17 00:00:00 2001 From: Matt McEuen <madgin@madgin.net> Date: Wed, 27 May 2020 15:43:34 -0500 Subject: [PATCH] Add site doc validation gate This adds a gate which loops over all phases in all sites, and performs an airshipctl apply --dry-run on them to ensure YAML validity and schema adherence. Aside from installation tasks, the gate is run via a makefile entrypoint so that it can be easily consumed by developers or by non-zuul CICD platforms. Change-Id: Ie4ab246848a580ab20c3153af1e3749a27e3f770 --- Makefile | 11 ++ ...airship-airshipctl-validate-documents.yaml | 28 ++++ tools/document/build_kustomize_plugin.sh | 46 +++++++ tools/document/get_kind.sh | 25 ++++ tools/document/start_kind.sh | 27 ++++ tools/document/validate_site_docs.sh | 127 ++++++++++++++++++ tools/validate_docs | 39 ++++++ zuul.d/jobs.yaml | 7 + zuul.d/projects.yaml | 2 + 9 files changed, 312 insertions(+) create mode 100644 playbooks/airship-airshipctl-validate-documents.yaml create mode 100755 tools/document/build_kustomize_plugin.sh create mode 100755 tools/document/get_kind.sh create mode 100755 tools/document/start_kind.sh create mode 100755 tools/document/validate_site_docs.sh create mode 100755 tools/validate_docs diff --git a/Makefile b/Makefile index fd969df70..c30513d7d 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,12 @@ GD_PORT ?= 8080 # Documentation location DOCS_DIR ?= docs +# document validation options +UNAME != uname +export KIND_URL ?= https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(UNAME)-amd64 +KUBECTL_VERSION ?= v1.16.2 +export KUBECTL_URL ?= https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl + .PHONY: depend depend: @go mod download @@ -206,3 +212,8 @@ add-copyright: .PHONY: check-copyright check-copyright: @./tools/check_copyright + +# Validate YAMLs for all sites +.PHONY: validate-docs +validate-docs: + @./tools/validate_docs diff --git a/playbooks/airship-airshipctl-validate-documents.yaml b/playbooks/airship-airshipctl-validate-documents.yaml new file mode 100644 index 000000000..9948a0f28 --- /dev/null +++ b/playbooks/airship-airshipctl-validate-documents.yaml @@ -0,0 +1,28 @@ +# 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. + +- hosts: primary + tasks: + + - name: Ensure kubectl is present + include_role: + name: install-kubectl + + - name: Ensure airshipctl is present + include_role: + name: airshipctl-systemwide-executable + + - name: Validating site documents + make: + target: validate-docs + chdir: "{{ zuul.project.src_dir }}" + diff --git a/tools/document/build_kustomize_plugin.sh b/tools/document/build_kustomize_plugin.sh new file mode 100755 index 000000000..de8a4787d --- /dev/null +++ b/tools/document/build_kustomize_plugin.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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. + +# Run this from the airshipctl project root +set -e + +# This script builds a version of kustomize that is able to acces +# the ReplacementTransformer plugin. +# It assumes a build airshipctl binary in bin/, or if not, +# somewhere on the path. +if [ -f "bin/airshipctl" ]; then + AIRSHIPCTL="bin/airshipctl" +else + AIRSHIPCTL="$(which airshipctl)" +fi + +: ${KUSTOMIZE_PLUGIN_HOME:="$HOME/.airship/kustomize-plugins"} +: ${AIRSHIP_TAG:="dev"} + +# purge any previous airship plugins +rm -rf ${KUSTOMIZE_PLUGIN_HOME}/airshipit.org + +# copy our plugin to the PLUGIN_ROOT, and give a kustomzie-friendly wrapper +PLUGIN_PATH=${KUSTOMIZE_PLUGIN_HOME}/airshipit.org/v1alpha1/replacementtransformer +mkdir -p ${PLUGIN_PATH} +cat > ${PLUGIN_PATH}/ReplacementTransformer <<EOF +#!/bin/bash +\$(dirname \$0)/airshipctl document plugin "\$@" +EOF +chmod +x ${PLUGIN_PATH}/ReplacementTransformer +cp -p ${AIRSHIPCTL} ${PLUGIN_PATH}/ + +# tell the user how to use this +echo -e "The airshipctl kustomize plugin has been installed.\nRun kustomize with:" +echo -e "KUSTOMIZE_PLUGIN_HOME=$KUSTOMIZE_PLUGIN_HOME \$GOPATH/bin/kustomize build --enable_alpha_plugins ..." diff --git a/tools/document/get_kind.sh b/tools/document/get_kind.sh new file mode 100755 index 000000000..7d7f4b696 --- /dev/null +++ b/tools/document/get_kind.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# 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. + +# This downloads kind, puts it in a temp directory, and prints the directory +set -e + +: ${KIND_URL:="https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(uname)-amd64"} +TMP=$(mktemp -d) +KIND="${TMP}/kind" + +curl -sSLo ${KIND} ${KIND_URL} +chmod +x ${KIND} + +echo ${TMP} diff --git a/tools/document/start_kind.sh b/tools/document/start_kind.sh new file mode 100755 index 000000000..8e6cef5bd --- /dev/null +++ b/tools/document/start_kind.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# 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. + +# This starts up a kubernetes cluster which is sufficient for +# assisting with tasks like `kubectl apply --dry-run` style validation +set -e + +: ${KIND:="/usr/local/bin/kind"} +: ${CLUSTER:="airship"} # NB: kind prepends "kind-" +: ${KUBECONFIG:="${HOME}/.airship/kubeconfig"} + +${KIND} create cluster --name ${CLUSTER} \ + --kubeconfig ${KUBECONFIG} + +echo "This cluster can be deleted via:" +echo "${KIND} delete cluster --name ${CLUSTER}" diff --git a/tools/document/validate_site_docs.sh b/tools/document/validate_site_docs.sh new file mode 100755 index 000000000..717e7011e --- /dev/null +++ b/tools/document/validate_site_docs.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# 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 -e + +: ${PROJECT_ROOT:=${PWD}} +: ${SITE:="test-workload"} +: ${CONTEXT:="kind-airship"} +: ${KUBECONFIG:="${HOME}/.airship/kubeconfig"} + +: ${KUBECTL:="/usr/local/bin/kubectl"} +: ${KUSTOMIZE_PLUGIN_HOME:="${HOME}/.airship/kustomize-plugins"} +TMP=$(mktemp -d) + +# Use the local project airshipctl binary as the default if it exists, +# otherwise use the one on the PATH +if [ -f "bin/airshipctl" ]; then + AIRSHIPCTL_DEFAULT="bin/airshipctl" +else + AIRSHIPCTL_DEFAULT="$(which airshipctl)" +fi + +: ${AIRSHIPCONFIG:="${TMP}/config"} +: ${AIRSHIPKUBECONFIG:="${TMP}/kubeconfig"} +: ${AIRSHIPCTL:="${AIRSHIPCTL_DEFAULT}"} +ACTL="${AIRSHIPCTL} --airshipconf ${AIRSHIPCONFIG} --kubeconfig ${AIRSHIPKUBECONFIG}" + +export KUSTOMIZE_PLUGIN_HOME +export KUBECONFIG + +# TODO: use `airshipctl config` to do this once all the needed knobs are exposed +# The non-default parts are to set the targetPath and subPath appropriately, +# and to craft up cluster/contexts to avoid the need for automatic kubectl reconciliation +function generate_airshipconf { + cluster=$1 + + cat <<EOL > ${AIRSHIPCONFIG} +apiVersion: airshipit.org/v1alpha1 +bootstrapInfo: + default: + builder: + networkConfigFileName: network-config + outputMetadataFileName: output-metadata.yaml + userDataFileName: user-data + container: + containerRuntime: docker + image: quay.io/airshipit/isogen:latest-debian_stable + volume: /srv/iso:/config + remoteDirect: + isoUrl: http://localhost:8099/debian-custom.iso +clusters: + ${CONTEXT}_${cluster}: + clusterType: + ${cluster}: + bootstrapInfo: default + clusterKubeconf: ${CONTEXT}_${cluster} + managementConfiguration: default +contexts: + ${CONTEXT}_${cluster}: + contextKubeconf: ${CONTEXT}_${cluster} + manifest: ${CONTEXT}_${cluster} +currentContext: ${CONTEXT}_${cluster} +kind: Config +managementConfiguration: + default: + insecure: true + systemActionRetries: 30 + systemRebootDelay: 30 + type: redfish +manifests: + ${CONTEXT}_${cluster}: + primaryRepositoryName: primary + repositories: + primary: + checkout: + branch: master + commitHash: "" + force: false + tag: "" + url: https://opendev.org/airship/treasuremap + subPath: manifests/site/${SITE} + targetPath: . +users: + ${CONTEXT}_${cluster}: {} +EOL +} + +# Loop over all cluster types and phases for the given site +for cluster in ephemeral target; do + # Clear out any CRDs left from testing of a previous cluster + ${KUBECTL} --context ${CONTEXT} --kubeconfig ${KUBECONFIG} delete crd --all > /dev/null + + if [[ -d "manifests/site/${SITE}/${cluster}" ]]; then + # Since we'll be mucking with the kubeconfig - make a copy of it and muck with the copy + cp ${KUBECONFIG} ${AIRSHIPKUBECONFIG} + # This is a big hack to work around kubeconfig reconciliation + # change the cluster name (as well as context and user) to avoid kubeconfig reconciliation + sed -i "s/${CONTEXT}/${CONTEXT}_${cluster}/" ${AIRSHIPKUBECONFIG} + generate_airshipconf ${cluster} + + for phase in $(ls manifests/site/${SITE}/${cluster}| grep -v "\.yaml$"); do + echo -e "\n*** Rendering ${cluster}/${phase}" + + # step 1: actually apply all crds in the phase + # TODO: will need to loop through phases in order, eventually + # e.g., load CRDs from initinfra first, so they're present when validating later phases + ${ACTL} phase render ${phase} -k CustomResourceDefinition > ${TMP}/crds.yaml + if [ -s ${TMP}/crds.yaml ]; then + ${KUBECTL} --context ${CONTEXT} --kubeconfig ${KUBECONFIG} apply -f ${TMP}/crds.yaml + fi + + # step 2: dry-run the entire phase + ${ACTL} phase apply --dry-run ${phase} + done + fi +done diff --git a/tools/validate_docs b/tools/validate_docs new file mode 100755 index 000000000..1bddde295 --- /dev/null +++ b/tools/validate_docs @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# 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. + +# The makefile entrypoint driver for document validation +# Expected to be run from the project root +set -e + +# get kind +echo "Fetching kind from ${KIND_URL}..." +TMP=$(KIND_URL=${KIND_URL} ./tools/document/get_kind.sh) +export KIND=${TMP}/kind +export KUBECTL_URL + +function cleanup() { + ${KIND} delete cluster --name airship + rm -rf ${TMP} +} +trap cleanup EXIT + +./tools/document/build_kustomize_plugin.sh +./tools/document/start_kind.sh + +for site in $(ls manifests/site); do + echo -e "\nValidating site: ${site}\n****************" + SITE=${site} ./tools/document/validate_site_docs.sh + echo "Validation of site ${site} is succesful!" +done + diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml index 0a4ba07df..2da77cdc6 100644 --- a/zuul.d/jobs.yaml +++ b/zuul.d/jobs.yaml @@ -45,6 +45,13 @@ - ^.*\.md$ - ^docs/.*$ +- job: + name: airship-airshipctl-validate-site-docs + pre-run: + - playbooks/airship-airshipctl-deploy-docker.yaml + run: playbooks/airship-airshipctl-validate-documents.yaml + nodeset: airship-airshipctl-single-node + - job: name: airship-airshipctl-functional-existing-k8s pre-run: playbooks/airship-airshipctl-deploy-existing-k8s.yaml diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml index a865c1734..629882342 100644 --- a/zuul.d/projects.yaml +++ b/zuul.d/projects.yaml @@ -22,6 +22,7 @@ - airship-airshipctl-lint-unit - airship-airshipctl-golint - airship-airshipctl-build-image + - airship-airshipctl-validate-site-docs # - airship-airshipctl-functional-existing-k8s TODO: Enable this when functional tests exist, and a cluster is up - airship-airshipctl-gate-test - airship-airshipctl-32GB-gate-test @@ -31,6 +32,7 @@ - airship-airshipctl-lint-unit - airship-airshipctl-lint-unit - airship-airshipctl-build-image + - airship-airshipctl-validate-site-docs # - airship-airshipctl-functional-existing-k8s TODO: Enable this when functional tests exist, and a cluster is up - airship-airshipctl-gate-test post: