diff --git a/.github/env b/.github/env index b84dad0..fb51957 100644 --- a/.github/env +++ b/.github/env @@ -1,3 +1,3 @@ golang-version=1.23 kind-version=v0.25.0 -kind-image=kindest/node:v1.31.2 +kind-image=kindest/node:v1.32.0 diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml index 780fd88..e5f68a6 100644 --- a/.github/workflows/e2e-tests.yaml +++ b/.github/workflows/e2e-tests.yaml @@ -17,8 +17,8 @@ env: HTTPS_ENABLE: true NETBOX_RESTORATION_HASH_FIELD_NAME: netboxOperatorRestorationHash jobs: - e2e-tests: - name: E2E tests for netbox operator + e2e-tests-3-7-8: + name: Against netbox version 3.7.8 runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -28,7 +28,6 @@ jobs: - name: Import environment variables from file run: | cat ".github/env" >> "$GITHUB_ENV" - echo "E2E_DIAGNOSTIC_DIRECTORY=$(mktemp -d)" >> "$GITHUB_ENV" - name: Start kind cluster uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 with: @@ -47,27 +46,70 @@ jobs: kubectl get pods -A echo "Cluster information" kubectl cluster-info - - name: Setup kind cluster with required software such as NetBox + - name: Run e2e tests run: | - make create-kind - - name: Deploy NetBox operator to the kind cluster + make test-e2e-3.7.8 + e2e-tests-4-0-11: + name: Against netbox version 4.0.11 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + with: + go-version: 1.23.4 + - name: Import environment variables from file run: | - make deploy-kind - - name: Run tests - env: - E2E_DIAGNOSTIC_DIRECTORY: ${{ env.E2E_DIAGNOSTIC_DIRECTORY }} + cat ".github/env" >> "$GITHUB_ENV" + - name: Start kind cluster + uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde # v1.10.0 + with: + version: ${{ env.kind-version }} + node_image: ${{ env.kind-image }} + wait: 300s + config: ./tests/e2e/kind-config.yaml + cluster_name: e2e + - name: Wait for cluster to finish bootstraping + run: | + echo "Waiting for all nodes to be ready..." + kubectl wait --for=condition=Ready nodes --all --timeout=120s + kubectl get nodes + echo "Waiting for all pods to be ready..." + kubectl wait --for=condition=Ready pods --all --all-namespaces --timeout=300s + kubectl get pods -A + echo "Cluster information" + kubectl cluster-info + - name: Run e2e tests run: | - # # Very straight forward way of implementing a test and checking the result - # kubectl apply -f config/samples/netbox_v1_prefixclaim.yaml - # kubectl get prefixclaim,prefix,ipaddressclaim,ipaddress,iprange,iprangeclaim - # kubectl wait --for=condition=ready --timeout=30s prefixclaim.netbox.dev/prefixclaim-sample - - # Use Chainsaw - make test-e2e - - name: Upload diagnostics artifact - if: ${{ failure() }} - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + make test-e2e-4.0.11 + e2e-tests-4-1-8: + name: Against netbox version 4.1.8 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + with: + go-version: 1.23.4 + - name: Import environment variables from file + run: | + cat ".github/env" >> "$GITHUB_ENV" + - name: Start kind cluster + uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde # v1.10.0 with: - name: cluster-state - path: ${{ env.E2E_DIAGNOSTIC_DIRECTORY }} - retention-days: 15 + version: ${{ env.kind-version }} + node_image: ${{ env.kind-image }} + wait: 300s + config: ./tests/e2e/kind-config.yaml + cluster_name: e2e + - name: Wait for cluster to finish bootstraping + run: | + echo "Waiting for all nodes to be ready..." + kubectl wait --for=condition=Ready nodes --all --timeout=120s + kubectl get nodes + echo "Waiting for all pods to be ready..." + kubectl wait --for=condition=Ready pods --all --all-namespaces --timeout=300s + kubectl get pods -A + echo "Cluster information" + kubectl cluster-info + - name: Run e2e tests + run: | + make test-e2e-4.1.8 diff --git a/.gitignore b/.gitignore index ea12f27..45297fe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,10 @@ Dockerfile.cross # Test binary, built with `go test -c` *.test -database.sql + +# Test/Intermediate files +kind/load-data-job/load-data.sh +kind/load-data-job/dockerfile # Output of the go coverage tool, specifically when used with LiteIDE *.out diff --git a/Makefile b/Makefile index abc4640..41e84b5 100644 --- a/Makefile +++ b/Makefile @@ -237,6 +237,25 @@ generate_mocks: ## TODO: auto install go install go.uber.org/mock/mockgen@latest mkdir -p ${GEN_DIR} mockgen -destination ${GEN_DIR}/${NETBOX_MOCKS_OUTPUT_FILE} -source=${INTERFACE_DEFITIONS_DIR} -.PHONY: test-e2e -test-e2e: install-$(GO_PACKAGE_NAME_CHAINSAW) - chainsaw test --namespace e2e +# e2e tests +E2E_PARAM := --namespace e2e --parallel 3 --apply-timeout 3m --assert-timeout 3m --delete-timeout 3m --error-timeout 3m --exec-timeout 3m # --skip-delete (add this argument for local debugging) +.PHONY: create-kind-3.7.8 +create-kind-3.7.8: + ./kind/local-env.sh --version 3.7.8 +.PHONY: test-e2e-3.7.8 +test-e2e-3.7.8: create-kind-3.7.8 deploy-kind install-$(GO_PACKAGE_NAME_CHAINSAW) + chainsaw test $(E2E_PARAM) + +.PHONY: create-kind-4.0.11 +create-kind-4.0.11: + ./kind/local-env.sh --version 4.0.11 +.PHONY: test-e2e-4.0.11 +test-e2e-4.0.11: create-kind-4.0.11 deploy-kind install-$(GO_PACKAGE_NAME_CHAINSAW) + chainsaw test $(E2E_PARAM) + +.PHONY: create-kind-4.1.8 +create-kind-4.1.8: + ./kind/local-env.sh --version 4.1.8 +.PHONY: test-e2e-4.1.8 +test-e2e-4.1.8: create-kind-4.1.8 deploy-kind install-$(GO_PACKAGE_NAME_CHAINSAW) + chainsaw test $(E2E_PARAM) diff --git a/kind/load-data-job/README.md b/kind/load-data-job/README.md new file mode 100644 index 0000000..40582c8 --- /dev/null +++ b/kind/load-data-job/README.md @@ -0,0 +1,7 @@ +# load-data-job + +Due to database schema changes cross major/minor NetBox versions, we have to `patch` the SQL files and demo data link on-the-fly. + +The default values stems from the NetBox 4.1.x version. So the patching will only happen for 3.7.x and 4.0.x versions. + +Please see `../local-env.sh`, that's where all the patching happen. diff --git a/kind/load-data-job/dockerfile b/kind/load-data-job/dockerfile.orig similarity index 62% rename from kind/load-data-job/dockerfile rename to kind/load-data-job/dockerfile.orig index 7435f45..447d0eb 100644 --- a/kind/load-data-job/dockerfile +++ b/kind/load-data-job/dockerfile.orig @@ -1,4 +1,4 @@ FROM python:3.12 ADD main.py . -RUN pip install pynetbox +RUN pip install -Iv pynetbox==7.4.1 CMD ["python", "./main.py"] diff --git a/kind/load-data-job/load-data.sh b/kind/load-data-job/load-data.orig.sh similarity index 96% rename from kind/load-data-job/load-data.sh rename to kind/load-data-job/load-data.orig.sh index 72d1ef4..a8cfe3b 100755 --- a/kind/load-data-job/load-data.sh +++ b/kind/load-data-job/load-data.orig.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -o errexit TMP_SQL_FILE=$(mktemp /tmp/netbox-data-dump.XXXXXXX.sql) || exit 1 curl -k https://raw.githubusercontent.com/netbox-community/netbox-demo-data/master/sql/netbox-demo-v4.1.sql > "${TMP_SQL_FILE}" diff --git a/kind/load-data-job/main.py b/kind/load-data-job/main.py index d9b99a1..5b2cef6 100644 --- a/kind/load-data-job/main.py +++ b/kind/load-data-job/main.py @@ -1,7 +1,6 @@ import pynetbox from pprint import pprint from dataclasses import dataclass -import sys print("Starting to load data onto NetBox through API") try: @@ -11,7 +10,7 @@ ) except pynetbox.RequestError as e: pprint(e.error) - sys.exit(1) + print("Connected to NetBoxAPI") # insert Tenants @@ -47,7 +46,7 @@ class Tenant: ) except pynetbox.RequestError as e: pprint(e.error) - sys.exit(1) + print("Tenants loaded") # insert Sites @@ -88,13 +87,14 @@ class Site: ) except pynetbox.RequestError as e: pprint(e.error) - sys.exit(1) + print("Sites loaded") # create custom fields and associate custom fields with IP/IPRange/Prefix @dataclass class CustomField: - object_types: list[str] + content_types: list[str] # for v3 + object_types: list[str] # for v4 type: str name: str label: str @@ -104,6 +104,7 @@ class CustomField: custom_fields = [ CustomField( + content_types=["ipam.ipaddress", "ipam.iprange", "ipam.prefix"], object_types=["ipam.ipaddress", "ipam.iprange", "ipam.prefix"], type="text", name="netboxOperatorRestorationHash", @@ -113,6 +114,7 @@ class CustomField: filter_logic="exact" ), CustomField( + content_types=["ipam.ipaddress", "ipam.iprange", "ipam.prefix"], object_types=["ipam.ipaddress", "ipam.iprange", "ipam.prefix"], type="text", name="example_field", @@ -122,6 +124,7 @@ class CustomField: filter_logic="exact" ), CustomField( + content_types=["ipam.prefix"], object_types=["ipam.prefix"], type="text", name="environment", @@ -131,6 +134,7 @@ class CustomField: filter_logic="exact" ), CustomField( + content_types=["ipam.prefix"], object_types=["ipam.prefix"], type="text", name="poolName", @@ -140,6 +144,7 @@ class CustomField: filter_logic="exact" ), CustomField( + content_types=["ipam.prefix"], object_types=["ipam.prefix"], type="boolean", name="cfDataTypeBool", @@ -149,6 +154,7 @@ class CustomField: filter_logic="exact" ), CustomField( + content_types=["ipam.prefix"], object_types=["ipam.prefix"], type="integer", name="cfDataTypeInteger", @@ -162,6 +168,7 @@ class CustomField: for custom_field in custom_fields: try: nb.extras.custom_fields.create( + content_types=custom_field.content_types, object_types=custom_field.object_types, type=custom_field.type, name=custom_field.name, @@ -173,7 +180,7 @@ class CustomField: ) except pynetbox.RequestError as e: pprint(e.error) - sys.exit(1) + print("Custom fields loaded") # for debugging @@ -461,5 +468,5 @@ class Prefix: ) except pynetbox.RequestError as e: pprint(e.error) - sys.exit(1) + print("Prefixes loaded") diff --git a/kind/local-env.sh b/kind/local-env.sh index 0aa6f80..69b6c83 100755 --- a/kind/local-env.sh +++ b/kind/local-env.sh @@ -1,24 +1,111 @@ #!/bin/bash -set -o errexit +set -e -u -o pipefail -kind create cluster || echo "cluster already exists, continuing..." +NAMESPACE="" +VERSION="4.1.8" # default value +NETBOX_HELM_CHART="https://github.com/netbox-community/netbox-chart/releases/download/netbox-5.0.0-beta.169/netbox-5.0.0-beta.169.tgz" # default value +while [[ $# -gt 0 ]]; do + case $1 in + -n|--namespace) + NAMESPACE="$2" + shift # past argument + shift # past value + ;; + -v|--version) + VERSION="$2" + shift # past argument + shift # past value + ;; + -*|--*) + echo "Unknown option $1" + exit 1 + ;; + esac +done + +echo "=======Parsed arguments=======" +echo "Namespace = ${NAMESPACE}" +echo "Version = ${VERSION}" +echo "==============================" -if [ -z "$1" ]; then - echo "Using default namespace." +# aurgment check / init +if [ -z "$NAMESPACE" ]; then + echo "Using default namespace" NAMESPACE="default" else - echo "Using namespace: $1" - NAMESPACE="$1" + echo "Using namespace: $NAMESPACE" fi -if ! kubectl get namespaces | grep -q "^${NAMESPACE} "; then - echo "Namespace ${NAMESPACE} does not exist." - exit 1 + +# create a kind cluster +kind create cluster || echo "cluster already exists, continuing..." +kubectl wait --for=jsonpath='{.status.phase}'=Active --timeout=1s namespace/${NAMESPACE} + +# load remote images +if [[ "${VERSION}" == "3.7.8" ]] ;then + echo "Using version ${VERSION}" + # need to align with netbox-chart otherwise the creation of the cluster will hang + declare -a Remote_Images=( \ + "gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1" \ + "busybox:1.36.1" \ + "docker.io/bitnami/redis:7.2.4-debian-12-r9" \ + "docker.io/netboxcommunity/netbox:v3.7.8" \ + "ghcr.io/zalando/postgres-operator:v1.12.2" \ + "ghcr.io/zalando/spilo-16:3.2-p3" \ + ) + NETBOX_HELM_CHART="https://github.com/netbox-community/netbox-chart/releases/download/netbox-5.0.0-beta5/netbox-5.0.0-beta5.tgz" + + # patch load-data.sh + sed 's/netbox-demo-v4.1.sql/netbox-demo-v3.7.sql/g' $(dirname "$0")/load-data-job/load-data.orig.sh > $(dirname "$0")/load-data-job/load-data.sh && chmod +x $(dirname "$0")/load-data-job/load-data.sh + + # patch dockerfile (See README at https://github.com/netbox-community/pynetbox for the supported version matrix) + sed 's/RUN pip install -Iv pynetbox==7.4.1/RUN pip install -Iv pynetbox==7.3.4/g' $(dirname "$0")/load-data-job/dockerfile.orig > $(dirname "$0")/load-data-job/dockerfile +elif [[ "${VERSION}" == "4.0.11" ]] ;then + echo "Using version ${VERSION}" + # need to align with netbox-chart otherwise the creation of the cluster will hang + declare -a Remote_Images=( \ + "gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1" \ + "busybox:1.36.1" \ + "docker.io/bitnami/redis:7.4.0-debian-12-r2" \ + "ghcr.io/netbox-community/netbox:v4.0.11" \ + "ghcr.io/zalando/postgres-operator:v1.12.2" \ + "ghcr.io/zalando/spilo-16:3.2-p3" \ + ) + NETBOX_HELM_CHART="https://github.com/netbox-community/netbox-chart/releases/download/netbox-5.0.0-beta.84/netbox-5.0.0-beta.84.tgz" + + # patch load-data.sh + sed 's/netbox-demo-v4.1.sql/netbox-demo-v4.0.sql/g' $(dirname "$0")/load-data-job/load-data.orig.sh > $(dirname "$0")/load-data-job/load-data.sh && chmod +x $(dirname "$0")/load-data-job/load-data.sh + + cp $(dirname "$0")/load-data-job/dockerfile.orig $(dirname "$0")/load-data-job/dockerfile +elif [[ "${VERSION}" == "4.1.8" ]] ;then + echo "Using version ${VERSION}" + # need to align with netbox-chart otherwise the creation of the cluster will hang + declare -a Remote_Images=( \ + "gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1" \ + "busybox:1.37.0" \ + "docker.io/bitnami/redis:7.4.1-debian-12-r2" \ + "ghcr.io/netbox-community/netbox:v4.1.8" \ + "ghcr.io/zalando/postgres-operator:v1.12.2" \ + "ghcr.io/zalando/spilo-16:3.2-p3" \ + ) + + # create load-data.sh + cp $(dirname "$0")/load-data-job/load-data.orig.sh $(dirname "$0")/load-data-job/load-data.sh + + cp $(dirname "$0")/load-data-job/dockerfile.orig $(dirname "$0")/load-data-job/dockerfile +else + echo "Unknown version ${VERSION}" + exit 1 fi +for img in "${Remote_Images[@]}"; do + docker pull "$img" + kind load docker-image "$img" +done + # build image for loading local data via NetBox API -cd ./kind/load-data-job && docker build -t netbox-load-local-data:1.0 --no-cache --progress=plain -f ./dockerfile . && cd - +cd ./kind/load-data-job && docker build -t netbox-load-local-data:1.0 --load --no-cache --progress=plain -f ./dockerfile . && cd - -# need to align with netbox-chart otherwise the creation of the cluster will hang +# load local images declare -a Local_Images=( \ "netbox-load-local-data:1.0" \ ) @@ -26,19 +113,7 @@ for img in "${Local_Images[@]}"; do kind load docker-image "$img" done -declare -a Remote_Images=( \ -"gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1" \ -"busybox:1.37.0" \ -"docker.io/bitnami/redis:7.4.1-debian-12-r2" \ -"ghcr.io/netbox-community/netbox:v4.1.7" \ -"ghcr.io/zalando/postgres-operator:v1.12.2" \ -"ghcr.io/zalando/spilo-16:3.2-p3" \ -) -for img in "${Remote_Images[@]}"; do - docker pull "$img" - kind load docker-image "$img" -done - +# install helm charts helm upgrade --install --namespace="${NAMESPACE}" postgres-operator \ https://opensource.zalando.com/postgres-operator/charts/postgres-operator/postgres-operator-1.12.2.tgz @@ -56,7 +131,7 @@ helm upgrade --install --namespace="${NAMESPACE}" netbox \ --set externalDatabase.existingSecretName="netbox.netbox-db.credentials.postgresql.acid.zalan.do" \ --set externalDatabase.existingSecretKey="password" \ --set redis.auth.password="password" \ - https://github.com/netbox-community/netbox-chart/releases/download/netbox-5.0.0-beta.163/netbox-5.0.0-beta.163.tgz + ${NETBOX_HELM_CHART} kubectl rollout status --namespace="${NAMESPACE}" deployment netbox @@ -64,3 +139,7 @@ kubectl rollout status --namespace="${NAMESPACE}" deployment netbox kubectl create job netbox-load-local-data --image=netbox-load-local-data:1.0 kubectl wait --namespace="${NAMESPACE}" --timeout=600s --for=condition=complete job/netbox-load-local-data docker rmi netbox-load-local-data:1.0 + +# clean up +rm $(dirname "$0")/load-data-job/load-data.sh +rm $(dirname "$0")/load-data-job/dockerfile