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/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 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