From 4ce61521e8b7b24c7b063d3138161a4843457e1c Mon Sep 17 00:00:00 2001 From: Aniruddh Kuthiala Date: Tue, 9 Dec 2025 20:24:46 +0000 Subject: [PATCH 1/2] Add CI to the github repo This change brings in several CI related changes: 1. On branch push, lint helm, lint go, run unit tests, validate CNAB, publish the docker image, publish the helm chart. 2. On branch push, also run container scanning using trivy and upload trivy results to the repo. 3. On tag cut (vX.Y.Z), publish the image to docker.io, publish the chart to docker.io, and bundle CNAB and upload to the marketplace ACR. --- .github/ISSUE_TEMPLATE/bug_report.md | 32 ------ .github/ISSUE_TEMPLATE/feature_request.md | 22 ---- .github/workflows/build-and-sign-image.yml | 101 ------------------ .github/workflows/dev-workflow.yaml | 115 +++++++++++++++++++++ .github/workflows/dockerhub-release.yaml | 77 ++++++++++++++ .github/workflows/run-scorecard.yml | 72 ------------- .github/workflows/run-tests.yml | 27 ----- CONTRIBUTING.md | 18 ++++ Makefile | 94 +++++++++++++++++ scripts/build.sh | 38 +++++++ scripts/cnab.sh | 81 +++++++++++++++ scripts/docker.sh | 106 +++++++++++++++++++ scripts/lint.sh | 7 ++ scripts/publish-helm.sh | 49 +++++++++ scripts/release.sh | 111 ++++++++++++++++++++ scripts/test.sh | 22 ++++ 16 files changed, 718 insertions(+), 254 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/workflows/build-and-sign-image.yml create mode 100644 .github/workflows/dev-workflow.yaml create mode 100644 .github/workflows/dockerhub-release.yaml delete mode 100644 .github/workflows/run-scorecard.yml delete mode 100644 .github/workflows/run-tests.yml create mode 100644 Makefile create mode 100755 scripts/build.sh create mode 100755 scripts/cnab.sh create mode 100755 scripts/docker.sh create mode 100755 scripts/lint.sh create mode 100755 scripts/publish-helm.sh create mode 100755 scripts/release.sh create mode 100755 scripts/test.sh diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index cc6c1d26..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' ---- -### Describe the bug - -A clear and concise description of what the bug is. - -### To reproduce - -Steps to reproduce the behavior: - -1. Deploy nginx_loadbalancer_kubernetes using -2. View output/logs/configuration on '...' -3. See error - -### Expected behavior - -A clear and concise description of what you expected to happen. - -### Your environment - -- Version of the nginx_loadbalancer_kubernetes or specific commit - -- Target deployment platform - -### Additional context - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index d27aba8e..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' ---- -### Is your feature request related to a problem? Please describe - -A clear and concise description of what the problem is. Ex. I'm always frustrated when ... - -### Describe the solution you'd like - -A clear and concise description of what you want to happen. - -### Describe alternatives you've considered - -A clear and concise description of any alternative solutions or features you've considered. - -### Additional context - -Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/build-and-sign-image.yml b/.github/workflows/build-and-sign-image.yml deleted file mode 100644 index 50b087d8..00000000 --- a/.github/workflows/build-and-sign-image.yml +++ /dev/null @@ -1,101 +0,0 @@ -# This workflow will build and push a signed Docker image - -name: Build and sign image - -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+" -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build_and_sign_image: - runs-on: ubuntu-latest - permissions: - contents: write - packages: write - id-token: write - security-events: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Clear Sigstore cache - run: rm -rf ~/.sigstore - - - uses: anchore/sbom-action@v0 - with: - image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - output-file: ./nginx-loadbalancer-kubernetes-${{env.GITHUB_REF_NAME}}.spdx.json - registry-username: ${{ github.actor }} - registry-password: ${{ secrets.GITHUB_TOKEN }} - - - name: Install cosign - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da #v3.7.0 - with: - cosign-release: 'v2.4.1' - - - name: Log into registry ${{ env.REGISTRY }} for ${{ github.actor }} - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@9dc751fe249ad99385a2583ee0d084c400eee04e - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build Docker Image - id: docker-build-and-push - uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 - with: - context: . - file: ./Dockerfile - push: true - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{github.run_number}} - - - name: Sign the published Docker images - env: - COSIGN_EXPERIMENTAL: "true" - # This step uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. - run: cosign sign --yes "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.docker-build-and-push.outputs.digest }}" - - # NOTE: This runs statically against the latest tag in Docker Hub which was not produced by this workflow - # This should be updated once this workflow is fully implemented - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@91713af97dc80187565512baba96e4364e983601 # 0.16.0 - continue-on-error: true - with: - image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest - format: 'sarif' - output: 'trivy-results-${{ inputs.image }}.sarif' - ignore-unfixed: 'true' - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v2.2.11 - continue-on-error: true - with: - sarif_file: 'trivy-results-${{ inputs.image }}.sarif' - sha: ${{ github.sha }} - ref: ${{ github.ref }} - - - name: Generate Release - uses: ncipollo/release-action@v1 - with: - artifacts: | - trivy-results-${{ inputs.image }}.sarif - ./nginx-loadbalancer-kubernetes-${{env.GITHUB_REF_NAME}}.spdx.json - body: | - # Release ${{env.GITHUB_REF_NAME}} - ## Changelog - ${{ steps.meta.outputs.changelog }} - generateReleaseNotes: true - makeLatest: false - name: "${{env.GITHUB_REF_NAME}}" diff --git a/.github/workflows/dev-workflow.yaml b/.github/workflows/dev-workflow.yaml new file mode 100644 index 00000000..5a1c4863 --- /dev/null +++ b/.github/workflows/dev-workflow.yaml @@ -0,0 +1,115 @@ +name: "dev-workflow" + +on: + workflow_dispatch: + push: + +permissions: + id-token: write + contents: read + actions: read + security-events: write + + +jobs: + lint_test_build: + runs-on: ubuntu-latest + + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + + steps: + - name: Checkout Repo + uses: actions/checkout@v5 + with: + fetch-depth: 2 + + - name: Azure Login via OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Docker Login to Azure Container Registry + run: | + registry_name=${{ secrets.DOCKER_REGISTRY_PROD }} + az acr login --name ${registry_name%%.*} + + - name: "lint + unit-test + build" + env: + DOCKER_REGISTRY_PROD: ${{ secrets.DOCKER_REGISTRY_PROD }} + run: | + if [ "$GITHUB_REF_NAME" != "$DEFAULT_BRANCH" ]; then + time make helm-lint + # Only run Go linting if Go files have changed + if git diff --name-only HEAD~1 HEAD | grep -E '\.(go|mod)$'; then + echo "Go files detected in changes, running Go linters..." + time make lint + git diff --exit-code + else + echo "No Go files changed, skipping Go linting..." + fi + time make test + fi + time make publish + time make publish-helm + + validate_cnab: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v5 + with: + fetch-depth: 2 + + - name: Azure Login via OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Docker Login to Azure Container Registry + run: | + registry_name=${{ secrets.DOCKER_REGISTRY_PROD }} + az acr login --name ${registry_name%%.*} + + - name: "validate-cnab" + run: time make validate-cnab + + security_scanning: + needs: [lint_test_build, validate_cnab] + runs-on: ubuntu-latest + + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + + steps: + - name: Checkout Repo + uses: actions/checkout@v5 + with: + fetch-depth: 2 + + - name: Azure Login via OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Docker Login to Azure Container Registry + run: | + registry_name=${{ secrets.DOCKER_REGISTRY_PROD }} + az acr login --name ${registry_name%%.*} + + - name: "NLK image scanning" + env: + DOCKER_REGISTRY_PROD: ${{ secrets.DOCKER_REGISTRY_PROD }} + run: time make scan-container-image + + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: results/trivy/trivy-results.sarif diff --git a/.github/workflows/dockerhub-release.yaml b/.github/workflows/dockerhub-release.yaml new file mode 100644 index 00000000..d22a8405 --- /dev/null +++ b/.github/workflows/dockerhub-release.yaml @@ -0,0 +1,77 @@ +name: dockerhub-release + +on: + push: + tags: + - 'v*' + +permissions: + id-token: write + contents: read + +jobs: + dockerhub-release: + runs-on: ubuntu-latest + if: > + startsWith(github.ref_name, 'v') && + github.ref_type == 'tag' && + contains(github.ref_name, '.') + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Azure Login via OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Docker Login to the Source Azure Container Registry + run: | + registry_name=${{ secrets.DOCKER_REGISTRY_PROD }} + az acr login --name ${registry_name%%.*} + + - name: Release Docker Image to Dockerhub + run: make release-docker-image + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + DOCKER_REGISTRY_PROD: ${{ secrets.DOCKER_REGISTRY_PROD }} + + - name: Release Helm Chart to Dockerhub + run: make release-helm-chart + env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + DOCKER_REGISTRY_PROD: ${{ secrets.DOCKER_REGISTRY_PROD }} + + cnab-release: + needs: [dockerhub-release] + runs-on: ubuntu-latest + + if: > + startsWith(github.ref_name, 'v') && + github.ref_type == 'tag' && + contains(github.ref_name, '.') + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Azure Login via OIDC + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_MARKETPLACE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_MARKETPLACE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_MARKETPLACE_SUBSCRIPTION_ID }} + + - name: Docker Login to the Marketplace Azure Container Registry + run: | + registry_name=${{ secrets.DOCKER_REGISTRY_MARKETPLACE }} + az acr login --name ${registry_name%%.*} + + - name: Release CNAB Bundle to Marketplace Registry + run: make release-cnab + env: + DOCKER_REGISTRY_PROD: ${{ secrets.DOCKER_REGISTRY_PROD }} diff --git a/.github/workflows/run-scorecard.yml b/.github/workflows/run-scorecard.yml deleted file mode 100644 index 3bbad843..00000000 --- a/.github/workflows/run-scorecard.yml +++ /dev/null @@ -1,72 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. They are provided -# by a third-party and are governed by separate terms of service, privacy -# policy, and support documentation. - -name: Scorecard supply-chain security -on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '15 14 * * 3' - push: - branches: [ "main" ] - -# Declare default permissions as read only. -permissions: read-all - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - # Uncomment the permissions below if installing in a private repository. - # contents: read - # actions: read - - steps: - - name: "Checkout code" - uses: actions/checkout@v4 # v3.1.0 - with: - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 - with: - results_file: results.sarif - results_format: sarif - # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: - # - you want to enable the Branch-Protection check on a *public* repository, or - # - you are installing Scorecard on a *private* repository - # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. - # repo_token: ${{ secrets.SCORECARD_TOKEN }} - - # Public repositories: - # - Publish results to OpenSSF REST API for easy access by consumers - # - Allows the repository to include the Scorecard badge. - # - See https://github.com/ossf/scorecard-action#publishing-results. - # For private repositories: - # - `publish_results` will always be set to `false`, regardless - # of the value entered here. - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - # Upload the results to GitHub's code scanning dashboard. - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 - with: - sarif_file: results.sarif diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml deleted file mode 100644 index 8d3d0691..00000000 --- a/.github/workflows/run-tests.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This workflow will build a golang project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go - -name: Run tests - -on: - branch_protection_rule: - types: - - created - -jobs: - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: 1.19 - - - name: Build - run: go build -v ./... - - - name: Test - run: go test -v ./... diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d86adb6..8e516318 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,24 @@ The following is a set of guidelines for contributing to the nginx_loadbalancer_ Follow our [Installation Guide](https://github.com/nginx/nginx-loadbalancer-kubernetes/blob/main/README.md#Installation) to get the nginx_loadbalancer_kubernetes up and running. +### Environment Configuration + +Before running build and publish make targets, you need to configure and export the required environment variables in a .env file. The `.env` file exports the container registry URLs (`DOCKER_REGISTRY_PROD`, `DOCKER_REGISTRY_DEV`) that are required by the build and publish make targets: + ```bash + $ cat .env + ... + export DOCKER_REGISTRY_PROD="prod.azurecr.io" + export DOCKER_REGISTRY_DEV="dev.azurecr.io" + ... + ``` + +1. **Source the environment file**: This file contains sensitive configuration and is not tracked in git. + + ```bash + source .env + ``` + + ## Contributing diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..6e251c17 --- /dev/null +++ b/Makefile @@ -0,0 +1,94 @@ +# - init general vars +ROOT_DIR = $(shell git rev-parse --show-toplevel) +BUILD_DIR = build +export BUILD_DIR +GO_BUILD_CACHE ?= .go-build +export GO_BUILD_CACHE +RESULTS_DIR = results +export RESULTS_DIR +VERSION = $(shell cat version) +GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) +export GITHUB_REF_NAME +CI ?= false +export CI + +DOCKER_REPO ?= nginx-azure-lb/nlb-devtools +DOCKER_TAG ?= latest +DEVTOOLS_IMG = $(DOCKER_REGISTRY_PROD)/$(DOCKER_REPO):$(DOCKER_TAG) +CNAB_IMG := mcr.microsoft.com/container-package-app:latest + +GITHUB_WORKSPACE ?= $(ROOT_DIR) + +.PHONY: .run all helm-lint lint test validate-cnab build-linux build-linux-docker publish publish-helm scan-container-image release-docker-image release-helm-chart release-cnab +.run: +ifeq ($(CI),true) + $(eval DOCKER_CI_SWITCH_ARGS = -e GOPATH=/tmp/gopath) +else + $(eval DOCKER_CI_SWITCH_ARGS = -it \ + -v $(shell go env GOPATH)/pkg:/go/pkg) +endif + @mkdir -p $(GO_BUILD_CACHE) + @docker run --rm \ + -v $(ROOT_DIR):$(ROOT_DIR) \ + -v $(ROOT_DIR)/.gitconfig:/.gitconfig \ + -v $(ROOT_DIR)/$(GO_BUILD_CACHE):/.cache/go-build \ + -w $(ROOT_DIR) \ + -e BUILD_DIR=$(BUILD_DIR) \ + -e RESULTS_DIR=$(RESULTS_DIR) \ + -e GITHUB_WORKSPACE=$(GITHUB_WORKSPACE) \ + -e VERSION=$(VERSION) \ + -e HOME=/tmp \ + --user $(shell id -u):$(shell id -g) \ + $(DOCKER_EXTRA_ARGS) \ + $(DOCKER_CI_SWITCH_ARGS) \ + $(or $(img),$(DEVTOOLS_IMG)) $(args) + +helm-lint: + @$(MAKE) .run args="helm lint charts/nlk" + +lint: + @$(MAKE) .run args="$(ROOT_DIR)/scripts/lint.sh" + +test: + @$(MAKE) .run args="$(ROOT_DIR)/scripts/test.sh" + +validate-cnab: + @$(MAKE) .run img="$(CNAB_IMG)" args="$(ROOT_DIR)/scripts/cnab.sh validate" + +build-linux: + @$(MAKE) .run args="$(ROOT_DIR)/scripts/build.sh linux" + +build-linux-docker: + @./scripts/docker.sh build + +publish: build-linux build-linux-docker + @./scripts/docker.sh publish + +publish-helm: + @scripts/publish-helm.sh + +scan-container-image: + @mkdir -p $(RESULTS_DIR)/trivy + @./scripts/docker.sh pull + @$(MAKE) .run DOCKER_EXTRA_ARGS=" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --group-add $(shell stat -c '%g' /var/run/docker.sock)" \ + args="trivy image nginxaas-loadbalancer-kubernetes:current" + @$(MAKE) .run DOCKER_EXTRA_ARGS=" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --group-add $(shell stat -c '%g' /var/run/docker.sock)" \ + args="trivy image --format sarif --output $(RESULTS_DIR)/trivy/trivy-results.sarif \ + nginxaas-loadbalancer-kubernetes:current" + +release-docker-image: + @./scripts/release.sh docker-image + +release-helm-chart: + @./scripts/release.sh helm-chart + +release-cnab: + @$(MAKE) .run img="$(CNAB_IMG)" DOCKER_EXTRA_ARGS=" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + --group-add $(shell stat -c '%g' /var/run/docker.sock) \ + -e CI=$(CI)" \ + args="$(ROOT_DIR)/scripts/cnab.sh package" diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 00000000..c071a19b --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -ex + +os="$1" + +if [[ -z $CI_COMMIT_SHORT_SHA ]]; then + CI_COMMIT_SHORT_SHA=$(git rev-parse --short=8 HEAD) +fi +if [[ -z $VERSION ]]; then + VERSION=$(cat version) +fi + +if [ "$os" == "linux" ]; then + export GOOS=linux + export GOARCH=amd64 + export CGO_ENABLED=0 +fi + +mkdir -p "$BUILD_DIR" + +pkg_path="./cmd/nginx-loadbalancer-kubernetes" +BUILDPKG="github.com/nginxinc/kubernetes-nginx-ingress/pkg/buildinfo" + +ldflags=( + # Set the value of the string variable in importpath named name to value. + -X "'$BUILDPKG.semVer=$VERSION'" + -X "'$BUILDPKG.shortHash=$CI_COMMIT_SHORT_SHA'" + -s # Omit the symbol table and debug information. + -w # Omit the DWARF symbol table. + -extldflags "'-fno-PIC'" +) + +go build \ + -v -tags "release osusergo" \ + -ldflags "${ldflags[*]}" \ + -o "${BUILD_DIR}/nginxaas-loadbalancer-kubernetes" \ + "$pkg_path" diff --git a/scripts/cnab.sh b/scripts/cnab.sh new file mode 100755 index 00000000..a5cc22f3 --- /dev/null +++ b/scripts/cnab.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +set -eo pipefail + +log() { + printf "\033[0;36m${*}\033[0m\n" >&2 +} + +package() { + check_ci + log "Packaging the CNAB bundle..." + CMD="cpa buildbundle -d ${BUNDLE_DIR} --telemetryOptOut" + ${CMD} +} + +validate() { + CMD="cpa verify -d ${BUNDLE_DIR} --telemetryOptOut" + ${CMD} + if ! git --no-pager diff --exit-code; then + log "Bundle validation failed: changes detected after verification." + exit 1 + fi +} + +set_version() { + VERSION=$(cat version) +} + +update_helm_chart() { + yq -ie '.global.azure.images.nlk.registry = .nlk.image.registry | .global.azure.images.nlk.image = .nlk.image.repository | .global.azure.images.nlk.tag = env(VERSION)' charts/nlk/values.yaml + yq -ie '.version = env(VERSION) | .appVersion = env(VERSION)' charts/nlk/Chart.yaml +} + +update_bundle() { + yq -ie '.version = env(VERSION)' charts/manifest.yaml +} + +check_ci() { + if [[ "${CI}" != "true" ]]; then + log "This script with the package arg is meant to be run in the CI." + exit 1 + fi +} + +set_vars() { + BUNDLE_DIR="${GITHUB_WORKSPACE}/charts/" +} + +install_yq() { + if ! command -v yq &> /dev/null; then + log "Installing yq..." + # Download yq binary to user bin + mkdir -p ~/bin + curl -sSfL "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64" -o ~/bin/yq + chmod +x ~/bin/yq + export PATH="$HOME/bin:$PATH" + fi +} + +main() { + install_yq + set_vars + set_version + update_helm_chart + update_bundle + local action="$1" + case "$action" in + validate) + validate + ;; + package) + package + ;; + *) + log "Action not supported." + exit 1 + ;; + esac +} + +main "$@" diff --git a/scripts/docker.sh b/scripts/docker.sh new file mode 100755 index 00000000..45ea3f95 --- /dev/null +++ b/scripts/docker.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +set -eo pipefail + +ROOT_DIR=$(git rev-parse --show-toplevel) + +log() { + printf "\033[0;36m${*}\033[0m\n" >&2 +} + +build() { + log "building image: $IMAGE" + DOCKER_BUILDKIT=1 docker build --target "$IMAGE" \ + --label VERSION="$version" \ + --label COMMIT="${CI_COMMIT_SHORT_SHA}" \ + --label PROJECT_NAME="${CI_PROJECT_NAME}" \ + --tag "${REPO}:${CI_COMMIT_REF_SLUG}" \ + --tag "${REPO}:${CI_COMMIT_REF_SLUG}-$version" \ + --tag "${REPO}:${CI_COMMIT_SHORT_SHA}" \ + --platform "linux/amd64" \ + -f "${ROOT_DIR}/Dockerfile" . +} + +pull() { + az acr login --name ${DEVOPS_DOCKER_URL%%.*} + log "pulling image: $IMAGE" + docker pull "${REPO}:${CI_COMMIT_REF_SLUG}" + docker tag "${REPO}:${CI_COMMIT_REF_SLUG}" "nginxaas-loadbalancer-kubernetes:current" +} + +publish() { + az acr login --name ${DEVOPS_DOCKER_URL%%.*} + docker push "$REPO:${CI_COMMIT_REF_SLUG}" + docker push "$REPO:${CI_COMMIT_REF_SLUG}-$version" + docker push "$REPO:${CI_COMMIT_SHORT_SHA}" + if [[ "$CI_COMMIT_REF_SLUG" == "${CI_DEFAULT_BRANCH}" ]]; then + docker tag "$REPO:${CI_COMMIT_SHORT_SHA}" "$REPO:latest" + docker tag "$REPO:${CI_COMMIT_SHORT_SHA}" "$REPO:$version" + docker push "$REPO:latest" + docker push "$REPO:$version" + fi + log "Published the following tags to the container registry:" + log " ${REPO}:${CI_COMMIT_REF_SLUG}" + log " ${REPO}:${CI_COMMIT_REF_SLUG}-$version" + log " ${REPO}:${CI_COMMIT_SHORT_SHA}" + if [[ "$CI_COMMIT_REF_SLUG" == "${CI_DEFAULT_BRANCH}" ]]; then + log " ${REPO}:latest" + log " ${REPO}:$version" + fi +} + +init_ci_vars() { + if [ -z "$CI_COMMIT_SHORT_SHA" ]; then + CI_COMMIT_SHORT_SHA=$(git rev-parse --short=8 HEAD) + fi + CI_PROJECT_NAME=nginxaas-loadbalancer-kubernetes + if [ -z "$CI_COMMIT_REF_SLUG" ]; then + CI_COMMIT_REF_SLUG=$( + git rev-parse --abbrev-ref HEAD | tr "[:upper:]" "[:lower:]" \ + | LANG=en_US.utf8 sed -E -e 's/[^a-zA-Z0-9]/-/g' -e 's/^-+|-+$$//g' \ + | cut -c 1-63 + ) + fi + if [ -z "$CI_DEFAULT_BRANCH" ]; then + CI_DEFAULT_BRANCH="main" + fi +} + +print_help () { + log "Usage: $(basename "$0") " +} + +parse_args() { + if [[ "$#" -ne 1 ]]; then + print_help + exit 0 + fi + + action="$1" + + valid_actions="(build|publish|pull)" + valid_actions_ptn="^${valid_actions}$" + if ! [[ "$action" =~ $valid_actions_ptn ]]; then + log "Invalid action. Valid actions: $valid_actions" + print_help + exit 1 + fi +} + +# MAIN +parse_args "$@" +init_ci_vars + +# shellcheck source=/dev/null +if [ "$CI" == "true" ]; then + DEVOPS_DOCKER_URL=$DOCKER_REGISTRY_PROD +else + DEVOPS_DOCKER_URL=$DOCKER_REGISTRY_DEV +fi +IMAGE="nginxaas-loadbalancer-kubernetes" +REPO="${DEVOPS_DOCKER_URL}/nginx-azure-lb/${CI_PROJECT_NAME}/$IMAGE" +# shellcheck source=/dev/null +# shellcheck disable=SC2153 +version=$(cat version) + +"$action" diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100755 index 00000000..5547de14 --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -x + +find . -type f -not -path "./.go/pkg/mod/*" -name \"*.go\" -exec goimports -e -w {} \+; +git diff --exit-code; +golangci-lint run -v ./...; diff --git a/scripts/publish-helm.sh b/scripts/publish-helm.sh new file mode 100755 index 00000000..3984c9c7 --- /dev/null +++ b/scripts/publish-helm.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +set -eo pipefail + +ROOT_DIR=$(git rev-parse --show-toplevel) + +log() { + printf "\033[0;36m${*}\033[0m\n" >&2 +} + +publish_helm() { + pkg="nginxaas-loadbalancer-kubernetes-${version}.tgz" + log "Packaging Helm chart..." + helm package --version "${version}" --app-version "${version}" charts/nlk + + log "Pushing Helm chart to registry..." + helm push "${pkg}" "${repo}" +} + +init_ci_vars() { + if [ -z "$CI_PROJECT_NAME" ]; then + CI_PROJECT_NAME=nginxaas-loadbalancer-kubernetes + fi + if [ -z "$CI_COMMIT_REF_SLUG" ]; then + CI_COMMIT_REF_SLUG=$( + git rev-parse --abbrev-ref HEAD | tr "[:upper:]" "[:lower:]" \ + | LANG=en_US.utf8 sed -E -e 's/[^a-zA-Z0-9]/-/g' -e 's/^-+|-+$$//g' \ + | cut -c 1-63 + ) + fi +} + +# MAIN +init_ci_vars + +# shellcheck source=/dev/null +if [ "$CI" == "true" ]; then + DEVOPS_DOCKER_URL=$DOCKER_REGISTRY_PROD +else + DEVOPS_DOCKER_URL=$DOCKER_REGISTRY_DEV +fi +log "Logging into the Helm registry..." +az acr login --name ${DEVOPS_DOCKER_URL%%.*} +repo="oci://${DEVOPS_DOCKER_URL}/nginx-azure-lb/${CI_PROJECT_NAME}/charts/${CI_COMMIT_REF_SLUG}" +# shellcheck source=/dev/null +# shellcheck disable=SC2153 +version=$(cat version) + +publish_helm diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 00000000..dfb2cb11 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +set -eo pipefail + +log() { + printf "\033[0;36m${*}\033[0m\n" >&2 +} + +docker-image() { + + SRC_REPO="${SRC_REGISTRY}/nginx-azure-lb/nginxaas-loadbalancer-kubernetes/nginxaas-loadbalancer-kubernetes" + SRC_IMG="${SRC_REPO}:main-${SRC_TAG}" + + DST_REPO="${DST_REGISTRY}/nginx/nginxaas-loadbalancer-kubernetes" + DST_IMG="${DST_REPO}:${DST_TAG}" + + log "Pulling image from source registry: ${SRC_IMG}" + docker pull "${SRC_IMG}" + + log "Tagging image for destination registry: ${DST_IMG}" + docker tag "${SRC_IMG}" "${DST_IMG}" + + log "Pushing image to destination registry: ${DST_IMG}" + docker push "${DST_IMG}" +} + +install_helm() { + if ! command -v helm >/dev/null; then + curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4 + chmod 700 get_helm.sh + ./get_helm.sh + fi +} + +helm-chart() { + install_helm + + SRC_REPO="${SRC_REGISTRY}/nginx-azure-lb/nginxaas-loadbalancer-kubernetes/charts/main/nginxaas-loadbalancer-kubernetes" + SRC_CHART="oci://${SRC_REPO}" + + DST_REPO="${DST_REGISTRY}/nginxcharts" + DST_CHART="oci://${DST_REPO}" + + + log "Pulling Helm chart from source registry: ${SRC_CHART}" + helm pull "${SRC_CHART}" --version "${SRC_TAG}" + + log "Pushing Helm chart to destination registry: ${DST_CHART}" + helm push nginxaas-loadbalancer-kubernetes-${DST_TAG}.tgz "${DST_CHART}" +} + + +help_text() { + log "Usage: $(basename $0) " +} + +set_docker_common() { + if [[ -z "${DOCKERHUB_USERNAME}" ]]; then + log "DOCKERHUB_USERNAME needs to be set." + exit 1 + fi + + if [[ -z "${DOCKERHUB_PASSWORD}" ]]; then + log "DOCKERHUB_PASSWORD needs to be set." + exit 1 + fi + SRC_REGISTRY="${DOCKER_REGISTRY_PROD}" + DST_REGISTRY="docker.io" + + log "Logging in to the destination registry..." + docker login --username "${DOCKERHUB_USERNAME}" --password "${DOCKERHUB_PASSWORD}" "${DST_REGISTRY}" + + SRC_TAG=$(echo "${GITHUB_REF_NAME}" | cut -f 2 -d "v") + DST_TAG="${SRC_TAG}" +} + +parse_args() { + if [[ "$#" -ne 1 ]]; then + help_text + exit 0 + fi + + artifact="${1}" + valid_artifact="(docker-image|helm-chart)" + valid_artifact_pttn="^${valid_artifact}$" + if ! [[ "${artifact}" =~ $valid_artifact_pttn ]]; then + log "Invalid artifact type. Valid artifact types: $valid_artifact" + help_text + exit 1 + fi +} + +main() { + if [[ "${CI}" != "true" ]]; then + log "This script is meant to be run in the CI." + exit 1 + fi + + pttn="^v[0-9]+\.[0-9]+\.[0-9]+" + if ! [[ "${GITHUB_REF_NAME}" =~ $pttn ]]; then + log "\${GITHUB_REF_NAME} needs to be set with valid semver format." + exit 1 + fi + + parse_args "$@" + ROOT_DIR=$(git rev-parse --show-toplevel) + set_docker_common + "$artifact" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..b7c4667c --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -ex + +export GO_DATA_RACE=${GO_DATA_RACE:-false} + +if [ "$GO_DATA_RACE" == "true" ]; then + go_flags+=("-race") +fi + +outfile="${RESULTS_DIR}/coverage.out" +mkdir -p "$RESULTS_DIR" +go_flags+=("-cover" -coverprofile="$outfile") + +set +e +gotestsum --junitfile "${RESULTS_DIR}/report.xml" --format pkgname -- "${go_flags[@]}" ./... +rc=$? +set -e +echo "Total code coverage:" +go tool cover -func="$outfile" | grep 'total:' | tee "${RESULTS_DIR}/anybadge.out" +go tool cover -html="$outfile" -o "${RESULTS_DIR}/coverage.html" +exit $rc From 592380f9535430596e664f7383034bc6b288c2ca Mon Sep 17 00:00:00 2001 From: Aniruddh Kuthiala Date: Tue, 9 Dec 2025 20:39:44 +0000 Subject: [PATCH 2/2] Update charts dir to prevent code skew --- charts/manifest.yaml | 2 +- charts/nlk/Chart.yaml | 4 ++-- charts/nlk/values.yaml | 16 ++++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/charts/manifest.yaml b/charts/manifest.yaml index 6883ea96..f6f6e6b9 100644 --- a/charts/manifest.yaml +++ b/charts/manifest.yaml @@ -1,7 +1,7 @@ applicationName: "marketplace/nginxaas-loadbalancer-kubernetes" publisher: "F5, Inc." description: "A component that manages NGINXaaS for Azure deployment and makes it act as Load Balancer for kubernetes workloads." -version: 0.4.0 +version: 1.2.4 helmChart: "./nlk" clusterArmTemplate: "./armTemplate.json" uiDefinition: "./createUIDefinition.json" diff --git a/charts/nlk/Chart.yaml b/charts/nlk/Chart.yaml index b11ab862..a5dd060e 100644 --- a/charts/nlk/Chart.yaml +++ b/charts/nlk/Chart.yaml @@ -1,6 +1,6 @@ --- apiVersion: v2 -appVersion: 0.8.0 +appVersion: 1.2.4 description: NGINXaaS LoadBalancer for Kubernetes name: nginxaas-loadbalancer-kubernetes keywords: @@ -13,4 +13,4 @@ maintainers: - name: "@chrisakker" - name: "@abdennour" type: application -version: 0.8.0 +version: 1.2.4 diff --git a/charts/nlk/values.yaml b/charts/nlk/values.yaml index e6f81470..7bf6afbb 100644 --- a/charts/nlk/values.yaml +++ b/charts/nlk/values.yaml @@ -3,11 +3,16 @@ # DO NOT REMOVE global: azure: - # images: - # nlk: - # registry: registry-1.docker.io - # image: nginx/nginxaas-loadbalancer-kubernetes - # tag: 0.4.0 + images: + nlk: + registry: registry-1.docker.io + image: nginx/nginxaas-loadbalancer-kubernetes + tag: 1.2.4 + # images: + # nlk: + # registry: registry-1.docker.io + # image: nginx/nginxaas-loadbalancer-kubernetes + # tag: 0.4.0 ##################################### nlk: name: nginxaas-loadbalancer-kubernetes @@ -32,7 +37,6 @@ nlk: config: ## trace,debug,info,warn,error,fatal,panic logLevel: "info" - ## the nginx hosts to send upstream updates to. For multiple hosts, use a sequence nginxHosts: "" ## Sets the annotation value that NLK is looking for to watch a Service