diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..a4a06fbd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,22 @@ +--- +name: Bug report 🐞 +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior e.g. provide example action definition. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..c2708426 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature Request 💡 +about: Suggest a new idea for the project. +labels: enhancement +--- + +# Summary + +Brief explanation of the feature. + +## Basic example + +If the proposal involves a new or changed API, include a basic code example. Omit this section if it's not applicable. + +## Motivation + +Why are we doing this? What use cases does it support? What is the expected outcome? \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 00000000..609870c0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,7 @@ +--- +name: Question ❓ +about: Is something unclear? +labels: question +--- + +# Question? \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..f6538752 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +--- +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + # Ignore K8 packages as these are done manually + ignore: + - dependency-name: "k8s.io/api" + - dependency-name: "k8s.io/apiextensions-apiserver" + - dependency-name: "k8s.io/apimachinery" + - dependency-name: "k8s.io/apiserver" + - dependency-name: "k8s.io/client-go" + - dependency-name: "k8s.io/component-base" + - dependency-name: "k8s.io/kube-aggregator" + - dependency-name: "k8s.io/kubectl" + - dependency-name: "sigs.k8s.io/controller-runtime" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..4ba787fc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,7 @@ +# Proposed Changes + +- +- +- + +Fixes # \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..c2dc12d5 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,64 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '⚠️ Breaking' + labels: + - 'breaking' + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - 'api-change' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + labels: + - 'chore' + - 'dependencies' + - 'marketing' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +exclude-labels: + - 'skip-changelog' +autolabeler: + - label: 'api-change' + files: + - '/api/*' + - label: 'documentation' + files: + - '*.md' + branch: + - '/docs{0,1}\/.+/' + - label: 'bug' + branch: + - '/fix\/.+/' + title: + - '/fix/i' + - label: 'enhancement' + branch: + - '/feature\/.+/' + body: + - '/JIRA-[0-9]{1,4}/' + - label: 'enhancement' + branch: + - '/enh\/.+/' + - label: 'chore' + branch: + - '/chore\/.+/' +template: | + ## Changes + $CHANGES diff --git a/.github/renovate.json b/.github/renovate.json deleted file mode 100644 index 30b0d522..00000000 --- a/.github/renovate.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended", - "default:pinDigestsDisabled", - "mergeConfidence:all-badges", - "docker:disable" - ], - "assignees": [ - "felix-kaestner" - ], - "commitMessageAction": "Renovate: Update", - "constraints": { - "go": "1.24" - }, - "dependencyDashboardOSVVulnerabilitySummary": "all", - "osvVulnerabilityAlerts": true, - "postUpdateOptions": [ - "gomodTidy", - "gomodUpdateImportPaths" - ], - "packageRules": [ - { - "matchPackageNames": [ - "golang" - ], - "allowedVersions": "1.24.x" - }, - { - "matchPackageNames": [ - "/^github\\.com\\/sapcc\\/.*/" - ], - "automerge": true, - "groupName": "github.com/sapcc" - }, - { - "matchPackageNames": [ - "!/^github\\.com\\/sapcc\\/.*/", - "/.*/" - ], - "matchUpdateTypes": [ - "minor", - "patch" - ], - "groupName": "External dependencies" - }, - { - "matchPackageNames": [ - "/^k8s.io\\//" - ], - "allowedVersions": "0.28.x" - } - ], - "prHourlyLimit": 0, - "schedule": [ - "before 8am on Friday" - ], - "semanticCommits": "disabled" -} diff --git a/.github/workflows/check-codegen.yml b/.github/workflows/check-codegen.yml new file mode 100644 index 00000000..e9699f64 --- /dev/null +++ b/.github/workflows/check-codegen.yml @@ -0,0 +1,30 @@ +name: Check Codegen + +on: + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + check-codegen: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - name: Run make generate + run: make generate + - name: Run make docs + run: make docs + - name: Run make helm + run: make helm + - name: Compare the expected and actual generated/* directories + run: | + if [ "$(git diff | wc -l)" -gt "0" ]; then + echo "Detected uncommitted changes after build. Consider running 'make generate && make docs && make helm'." + echo "See status below:" + git diff + exit 1 + fi diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml deleted file mode 100644 index 6b679780..00000000 --- a/.github/workflows/checks.yaml +++ /dev/null @@ -1,56 +0,0 @@ -################################################################################ -# This file is AUTOGENERATED with # -# Edit Makefile.maker.yaml instead. # -################################################################################ - -# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company -# SPDX-License-Identifier: Apache-2.0 - -name: Checks -"on": - push: - branches: - - main - pull_request: - branches: - - '*' - workflow_dispatch: {} -permissions: - checks: write - contents: read -jobs: - checks: - name: Checks - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - check-latest: true - go-version: 1.24.4 - - name: Run prepare make target - run: make generate - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v8 - with: - version: latest - - name: Run shellcheck - uses: ludeeus/action-shellcheck@2.0.0 - - name: Dependency Licenses Review - run: make check-dependency-licenses - - name: Check for spelling errors - uses: reviewdog/action-misspell@v1 - with: - exclude: ./vendor/* - fail_on_error: true - github_token: ${{ secrets.GITHUB_TOKEN }} - ignore: importas - reporter: github-check - - name: Check if source code files have license header - run: make check-addlicense - - name: Install govulncheck - run: go install golang.org/x/vuln/cmd/govulncheck@latest - - name: Run govulncheck - run: govulncheck -format text ./... diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 6309367a..00000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,56 +0,0 @@ -################################################################################ -# This file is AUTOGENERATED with # -# Edit Makefile.maker.yaml instead. # -################################################################################ - -# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company -# SPDX-License-Identifier: Apache-2.0 - -name: CI -"on": - push: - branches: - - main - paths-ignore: - - '**.md' - pull_request: - branches: - - '*' - paths-ignore: - - '**.md' - workflow_dispatch: {} -permissions: - contents: read -jobs: - build: - name: Build - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - check-latest: true - go-version: 1.24.4 - - name: Run prepare make target - run: make generate - - name: Build all binaries - run: make build-all - test: - name: Test - needs: - - build - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - check-latest: true - go-version: 1.24.4 - - name: Run prepare make target - run: make generate - - name: Run tests and generate coverage report - run: make build/cover.out diff --git a/.github/workflows/clean-cache.yml b/.github/workflows/clean-cache.yml new file mode 100644 index 00000000..65493c79 --- /dev/null +++ b/.github/workflows/clean-cache.yml @@ -0,0 +1,31 @@ +name: Cleanup caches by a branch +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + REPO=${{ github.repository }} + BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/goreleaser.yaml b/.github/workflows/goreleaser.yaml deleted file mode 100644 index 3c4527c7..00000000 --- a/.github/workflows/goreleaser.yaml +++ /dev/null @@ -1,44 +0,0 @@ -################################################################################ -# This file is AUTOGENERATED with # -# Edit Makefile.maker.yaml instead. # -################################################################################ - -# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company -# SPDX-License-Identifier: Apache-2.0 - -name: goreleaser -"on": - push: - tags: - - '*' -permissions: - contents: write - packages: write -jobs: - release: - name: goreleaser - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up Go - uses: actions/setup-go@v5 - with: - check-latest: true - go-version: 1.24.4 - - name: Run prepare make target - run: make generate - - name: Generate release info - run: | - go install github.com/sapcc/go-bits/tools/release-info@latest - mkdir -p build - release-info CHANGELOG.md "$(git describe --tags --abbrev=0)" > build/release-info - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 - with: - args: release --clean --release-notes=./build/release-info - version: latest - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/kustomize-validation.yml b/.github/workflows/kustomize-validation.yml new file mode 100644 index 00000000..c381bc85 --- /dev/null +++ b/.github/workflows/kustomize-validation.yml @@ -0,0 +1,20 @@ +name: Kustomize Validation + +on: + pull_request: + types: [ assigned, opened, synchronize, reopened ] + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + kustomize-validation: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - uses: imranismail/setup-kustomize@v2 + with: + kustomize-version: '5.0.0' + - run: | + ./hack/validate-kustomize.sh diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..cd69b4d8 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,20 @@ +name: Lint + +on: + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.1 diff --git a/.github/workflows/publish-chart.yml b/.github/workflows/publish-chart.yml new file mode 100644 index 00000000..b2a9dc1e --- /dev/null +++ b/.github/workflows/publish-chart.yml @@ -0,0 +1,84 @@ +name: Release Helm Chart + +on: + release: + types: + - published + push: + branches: + - main + tags: + - 'v*.*.*' + paths-ignore: + - 'docs/**' + - '**/*.md' + pull_request: + branches: + - main + paths-ignore: + - 'docs/**' + - '**/*.md' + types: [labeled, opened, synchronize, reopened] + +jobs: + helm-chart: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + if: | + github.event_name == 'push' || + (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ok-to-charts')) || + (github.event_name == 'release' && github.event.action == 'published') + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: v3.16.2 + + - name: Determine chart version + id: chart_version + run: | + if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then + # Use SHA for main branch + CHART_VERSION="0.0.0-$(echo ${{ github.sha }} | cut -c1-7)" + elif [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Use tag version (strip 'v' prefix) + CHART_VERSION="${GITHUB_REF#refs/tags/v}" + else + # Use PR SHA for dry run + CHART_VERSION="0.0.0-$(echo ${{ github.sha }} | cut -c1-7)" + fi + echo "version=$CHART_VERSION" >> $GITHUB_OUTPUT + + - name: Install Kustomize + run: | + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + mv kustomize /usr/local/bin + + - name: Package Helm chart with crds folder in template + run: | + helm package dist/chart --version ${{ steps.chart_version.outputs.version }}-crds + + - name: Prepare CRDs folder + run: | + mkdir -p dist/chart/crds + kustomize build config/default | yq ea 'select(.kind == "CustomResourceDefinition")' > dist/chart/crds/crds.yaml + rm -rf dist/chart/templates/crd + + - name: Package Helm chart with removed crds folder from template folder + run: | + helm package dist/chart --version ${{ steps.chart_version.outputs.version }} + + - name: Log in to GitHub Container Registry + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Push Helm chart to GHCR + run: | + helm push network-operator-${{ steps.chart_version.outputs.version }}.tgz oci://ghcr.io/${{ github.repository_owner }}/charts + helm push network-operator-${{ steps.chart_version.outputs.version }}-crds.tgz oci://ghcr.io/${{ github.repository_owner }}/charts diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml new file mode 100644 index 00000000..ee5ed46a --- /dev/null +++ b/.github/workflows/publish-docker.yml @@ -0,0 +1,85 @@ +name: Build and Publish Docker Image + +on: + release: + types: + - published + push: + branches: + - main + tags: + - v* + paths-ignore: + - 'docs/**' + - '**/*.md' + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + types: [labeled, unlabeled, opened, synchronize, reopened] + +jobs: + buildAndPush: + strategy: + matrix: + image: + - name: network-operator-controller-manager + target: manager + permissions: + contents: read + packages: write + # Condition: Run on push to main, published release, OR PR with 'ok-to-image' label + if: | + github.event_name == 'push' || + (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'ok-to-image')) || + (github.event_name == 'release' && github.event.action == 'published') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/metadata-action@v5 + id: meta + with: + images: | + ghcr.io/${{ github.repository_owner }}/${{ matrix.image.name }} + tags: | + type=semver,pattern={{version}} + type=schedule + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=sha + flavor: | + latest=${{ github.ref == 'refs/heads/main' }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + # workaround for self-hosted runner + # https://github.com/mumoshu/actions-runner-controller-ci/commit/e91c8c0f6ca82aa7618010c6d2f417aa46c4a4bf + - name: Set up Docker Context for Buildx + id: buildx-context + run: | + docker context create builders + - name: Set up Docker Buildx + timeout-minutes: 5 + uses: docker/setup-buildx-action@v3 + with: + version: latest + endpoint: builders # self-hosted + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + timeout-minutes: 40 + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + target: ${{ matrix.image.target }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000..327c5ab2 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,26 @@ +name: Release Drafter + +on: + push: + branches: + - main + pull_request_target: + types: [ opened, reopened, synchronize ] + workflow_dispatch: + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "main" + - uses: release-drafter/release-drafter@v6 + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/reuse.yaml b/.github/workflows/reuse.yaml deleted file mode 100644 index 7597d554..00000000 --- a/.github/workflows/reuse.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors -# SPDX-License-Identifier: Apache-2.0 - -name: REUSE Compliance -on: - push: - branches: - - main - pull_request: - branches: - - '*' - workflow_dispatch: {} -permissions: - contents: read -jobs: - test: - name: Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: REUSE Compliance Check - uses: fsfe/reuse-action@v5 diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml new file mode 100644 index 00000000..c493d346 --- /dev/null +++ b/.github/workflows/reuse.yml @@ -0,0 +1,12 @@ +name: REUSE Compliance Check + +on: pull_request + +jobs: + test: + name: reuse + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: REUSE Compliance Check + uses: fsfe/reuse-action@v5 diff --git a/.github/workflows/size-label.yml b/.github/workflows/size-label.yml new file mode 100644 index 00000000..77cc98b1 --- /dev/null +++ b/.github/workflows/size-label.yml @@ -0,0 +1,20 @@ +name: Size Label + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +jobs: + size-label: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: size-label + uses: pascalgn/size-label-action@v0.5.5 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml deleted file mode 100644 index 79d72896..00000000 --- a/.github/workflows/stale.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors -# SPDX-License-Identifier: Apache-2.0 - -name: Close inactive issues -on: - schedule: - - cron: "35 1 * * *" - -jobs: - close-issues: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: actions/stale@v9 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - days-before-issue-stale: 90 - days-before-issue-close: 14 - days-before-pr-stale: 45 - days-before-pr-close: 14 - stale-issue-label: "stale" - stale-issue-message: "This issue is stale because it has been open for 90 days with no activity." - close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." - stale-pr-label: 'stale' - stale-pr-message: "This PR is stale because it has been open for 45 days with no activity." - close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale." - exempt-issue-labels: "pinned,security,backlog,bug" - exempt-pr-labels: "pinned,security,backlog,bug" - exempt-draft-pr: true - diff --git a/.github/workflows/test-chart.yml b/.github/workflows/test-chart.yml index 5b98ba15..e8350d21 100644 --- a/.github/workflows/test-chart.yml +++ b/.github/workflows/test-chart.yml @@ -1,94 +1,72 @@ -# SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors -# SPDX-License-Identifier: Apache-2.0 +name: Test Chart + +permissions: + contents: read -name: Test on: - push: - branches: - - main - paths-ignore: - - '**.md' pull_request: - branches: - - '*' paths-ignore: - - '**.md' - workflow_dispatch: {} -permissions: - contents: read + - 'docs/**' + - '**/*.md' + jobs: - test-chart: - name: Chart + test-e2e: + name: Run on Ubuntu runs-on: ubuntu-latest steps: - - name: Check out code + - name: Clone the code uses: actions/checkout@v4 - - name: Set up Go + + - name: Setup Go uses: actions/setup-go@v5 with: - check-latest: true - go-version: 1.24.4 - - name: Fetch latest kubectl version - id: kubectl - run: | - KUBECTL_VERSION=$(curl -sL https://dl.k8s.io/release/stable.txt) - echo "version=$KUBECTL_VERSION" >> $GITHUB_OUTPUT - - name: Fetch latest kind version - id: kind - run: | - KIND_VERSION=$(curl -s https://api.github.com/repos/kubernetes-sigs/kind/releases/latest | grep '"tag_name":' | cut -d'"' -f4) - echo "version=$KIND_VERSION" >> $GITHUB_OUTPUT - - name: Create k8s kind cluster - uses: helm/kind-action@v1 - with: - version: ${{ steps.kind.outputs.version }} - cluster_name: kind - kubectl_version: ${{ steps.kubectl.outputs.version }} - - name: Prepare network-operator - run: | - go mod tidy - make docker-build IMG=network-operator:v0.1.0 - kind load docker-image network-operator:v0.1.0 + go-version-file: go.mod + - name: Install Helm run: | curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + - name: Verify Helm installation run: helm version + - name: Lint Helm Chart run: | - helm lint ./charts/network-operator -# TODO: Uncomment if cert-manager is enabled -# - name: Install cert-manager via Helm -# run: | -# helm repo add jetstack https://charts.jetstack.io -# helm repo update -# helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true -# - name: Wait for cert-manager to be ready -# run: | -# kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager -# kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-cainjector -# kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-webhook -# TODO: Uncomment if Prometheus is enabled -# - name: Install Prometheus Operator CRDs -# run: | -# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts -# helm repo update -# helm install prometheus-crds prometheus-community/prometheus-operator-crds -# - name: Install Prometheus via Helm -# run: | -# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts -# helm repo update -# helm install prometheus prometheus-community/prometheus --namespace monitoring --create-namespace -# - name: Wait for Prometheus to be ready -# run: | -# kubectl wait --namespace monitoring --for=condition=available --timeout=300s deployment/prometheus-server + helm lint ./dist/chart + + - name: Install the latest version of kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Create kind cluster + run: kind create cluster + + - name: Prepare network-operator + run: | + go mod download + make docker-build CONTROLLER_IMG=network-operator:v0.1.0 + kind load docker-image network-operator:v0.1.0 + + - name: Install cert-manager via Helm + run: | + helm repo add jetstack https://charts.jetstack.io + helm repo update + helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true + + - name: Wait for cert-manager to be ready + run: | + kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager + kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-cainjector + kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-webhook + - name: Install Helm chart for project run: | - helm install my-release ./charts/network-operator --create-namespace --namespace network-operator-system + helm install my-release ./dist/chart --create-namespace --namespace network-operator-system + - name: Check Helm release status run: | helm status my-release --namespace network-operator-system -# TODO: Uncomment if prometheus.enabled is set to true to confirm that the ServiceMonitor gets created -# - name: Check Presence of ServiceMonitor -# run: | -# kubectl wait --namespace network-operator-system --for=jsonpath='{.kind}'=ServiceMonitor servicemonitor/network-operator-controller-manager-metrics-monitor diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index b08da279..6fbe1de8 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -1,50 +1,38 @@ -# SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors -# SPDX-License-Identifier: Apache-2.0 +name: E2E Tests -name: Test on: - push: - branches: - - main - paths-ignore: - - '**.md' pull_request: - branches: - - '*' + types: [ assigned, opened, synchronize, reopened ] paths-ignore: - - '**.md' - workflow_dispatch: {} -permissions: - contents: read + - 'docs/**' + - '**/*.md' + jobs: test-e2e: - name: E2E + name: Run on Ubuntu runs-on: ubuntu-latest steps: - - name: Check out code + - name: Clone the code uses: actions/checkout@v4 - - name: Set up Go + + - name: Setup Go uses: actions/setup-go@v5 with: - check-latest: true - go-version: 1.24.4 - - name: Fetch latest kubectl version - id: kubectl - run: | - KUBECTL_VERSION=$(curl -sL https://dl.k8s.io/release/stable.txt) - echo "version=$KUBECTL_VERSION" >> $GITHUB_OUTPUT - - name: Fetch latest kind version - id: kind + go-version-file: go.mod + + - name: Install the latest version of kind run: | - KIND_VERSION=$(curl -s https://api.github.com/repos/kubernetes-sigs/kind/releases/latest | grep '"tag_name":' | cut -d'"' -f4) - echo "version=$KIND_VERSION" >> $GITHUB_OUTPUT - - name: Create k8s kind cluster - uses: helm/kind-action@v1 - with: - version: ${{ steps.kind.outputs.version }} - cluster_name: kind - kubectl_version: ${{ steps.kubectl.outputs.version }} - - name: Running E2E Tests + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Create kind cluster + run: kind create cluster + + - name: Running Test e2e run: | go mod tidy make test-e2e diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..93b84db5 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,19 @@ +name: Pull Request Code test + +on: + pull_request: + types: [ assigned, opened, synchronize, reopened ] + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + checks: + name: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - run: make test diff --git a/.goreleaser.yaml b/.goreleaser.yaml deleted file mode 100644 index 1902b5f4..00000000 --- a/.goreleaser.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# yaml-language-server: $schema=https://goreleaser.com/static/schema.json -# SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company -# SPDX-License-Identifier: Apache-2.0 -version: 2 - -archives: - - name_template: '{{ .ProjectName }}-{{ replace .Version "v" "" }}-{{ .Os }}-{{ .Arch }}' - format_overrides: - - goos: windows - format: zip - files: - - CHANGELOG.md - - LICENSE - - README.md - -checksum: - name_template: "checksums.txt" - -builds: - - binary: 'network-operator' - env: - - CGO_ENABLED=0 - goos: - - linux - - windows - - darwin - goarch: - - amd64 - - arm64 - ignore: - - goos: windows - goarch: arm64 - ldflags: - - -s -w - - -X github.com/sapcc/go-api-declarations/bininfo.binName=network-operator - - -X github.com/sapcc/go-api-declarations/bininfo.version={{ .Version }} - - -X github.com/sapcc/go-api-declarations/bininfo.commit={{ .FullCommit }} - - -X github.com/sapcc/go-api-declarations/bininfo.buildDate={{ .CommitDate }} # use CommitDate instead of Date for reproducibility - main: ./cmd/manager - # Set the modified timestamp on the output binary to ensure that builds are reproducible. - mod_timestamp: "{{ .CommitTimestamp }}" - -release: - make_latest: true - prerelease: auto - -snapshot: - version_template: "{{ .Tag }}-next" diff --git a/Dockerfile b/Dockerfile index 69a56cc2..a9126d0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,43 +2,54 @@ # SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors # SPDX-License-Identifier: Apache-2.0 -FROM golang:1.24-alpine3.22 AS builder - -RUN apk add --no-cache --no-progress git make - -ARG BININFO_BUILD_DATE -ARG BININFO_COMMIT_HASH -ARG BININFO_VERSION - +# Build the manager binary +FROM --platform=$BUILDPLATFORM golang:1.24.4 AS builder ARG TARGETOS ARG TARGETARCH WORKDIR /workspace - +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=bind,source=go.mod,target=go.mod \ --mount=type=bind,source=go.sum,target=go.sum \ go mod download -x -RUN --mount=type=bind,target=.,readwrite \ - --mount=type=cache,target=/go/pkg/mod \ - --mount=type=cache,target=/root/.cache/go-build \ - GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOTOOLCHAIN=local make PREFIX=/pkg install - -FROM gcr.io/distroless/static:nonroot - ARG BININFO_BUILD_DATE ARG BININFO_COMMIT_HASH ARG BININFO_VERSION +# Copy the go source +COPY cmd/manager/main.go cmd/manager/main.go +#COPY api/ api/ +COPY internal/ internal/ + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +FROM builder AS manager-builder +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/manager/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot AS manager +ARG BININFO_BUILD_DATE +ARG BININFO_COMMIT_HASH +ARG BININFO_VERSION LABEL source_repository="https://github.com/ironcore-dev/network-operator" \ org.opencontainers.image.url="https://github.com/ironcore-dev/network-operator" \ org.opencontainers.image.created=${BININFO_BUILD_DATE} \ org.opencontainers.image.revision=${BININFO_COMMIT_HASH} \ org.opencontainers.image.version=${BININFO_VERSION} - -COPY --from=builder /pkg/ /usr/ - -USER 65532:65532 WORKDIR / -ENTRYPOINT [ "/usr/bin/network-operator" ] +COPY --from=manager-builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index 24aa71d6..e8de7b61 100644 --- a/Makefile +++ b/Makefile @@ -1,339 +1,314 @@ -################################################################################ -# This file is AUTOGENERATED with # -# Edit Makefile.maker.yaml instead. # -################################################################################ - -# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company -# SPDX-License-Identifier: Apache-2.0 - -MAKEFLAGS=--warn-undefined-variables -# /bin/sh is dash on Debian which does not support all features of ash/bash -# to fix that we use /bin/bash only on Debian to not break Alpine -ifneq (,$(wildcard /etc/os-release)) # check file existence - ifneq ($(shell grep -c debian /etc/os-release),0) - SHELL := /bin/bash - endif -endif -UNAME_S := $(shell uname -s) -SED = sed -XARGS = xargs -ifeq ($(UNAME_S),Darwin) - SED = gsed - XARGS = gxargs -endif -default: build-all +# Image URL to use all building/pushing image targets +CONTROLLER_IMG ?= controller:latest + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif -# Image to use all building/pushing image targets -IMG ?= controller:latest +GOARCH := $(shell go env GOARCH) +GOOS := $(shell go env GOOS) # CONTAINER_TOOL defines the container tool to be used for building images. -# The default is docker, but it can be overridden to use other tools (i.e. podman or nerdctl). +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) CONTAINER_TOOL ?= docker -# KIND_CLUSTER_NAME defines the name of the Kind cluster to be used for the tilt setup. -KIND_CLUSTER_NAME ?= network - -install-gofumpt: FORCE - @if ! hash gofumpt 2>/dev/null; then printf "\e[1;36m>> Installing gofumpt...\e[0m\n"; go install mvdan.cc/gofumpt@latest; fi - -install-kustomize: FORCE - @if ! hash kustomize 2>/dev/null; then printf "\e[1;36m>> Installing kustomize...\e[0m\n"; go install sigs.k8s.io/kustomize/kustomize/v5@latest; fi - -fmt: FORCE install-gofumpt - @printf "\e[1;36m>> gofumpt -l -w .\e[0m\n" - @gofumpt -l -w . - -# Run the e2e tests against a k8s cluster. -test-e2e: FORCE +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk command is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: goimports ## Run goimports against code. + $(GOIMPORTS) -w . + + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet setup-envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. @command -v kind >/dev/null 2>&1 || { \ - echo "Kind is not installed. Please install Kind manually."; \ - exit 1; \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ } @kind get clusters | grep -q 'kind' || { \ - echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ - exit 1; \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ } - @printf "\e[1;36m>> go test ./test/e2e/ -v -ginkgo.v\e[0m\n" - @go test ./test/e2e/ -v -ginkgo.v - -docker-build: FORCE - @printf "\e[1;36m>> $(CONTAINER_TOOL) build --tag=$(IMG) .\e[0m\n" - @$(CONTAINER_TOOL) build --build-arg=BININFO_BUILD_DATE=$(BININFO_BUILD_DATE) --build-arg=BININFO_COMMIT_HASH=$(BININFO_COMMIT_HASH) --build-arg=BININFO_VERSION=$(BININFO_VERSION) --tag=$(IMG) . - -docker-push: FORCE - @printf "\e[1;36m>> $(CONTAINER_TOOL) push $(IMG)\e[0m\n" - @$(CONTAINER_TOOL) push $(IMG) - -# Generate a consolidated YAML with CRDs and deployment. -build-installer: FORCE generate install-kustomize - @printf "\e[1;36m>> kustomize build config/default > dist/install.yaml\e[0m\n" - @cd config/manager && kustomize edit set image controller=$(IMG) - @mkdir -p build; kustomize build config/default > dist/install.yaml - -# Deploy controller to the k8s cluster -deploy: FORCE generate install-kustomize - @printf "\e[1;36m>> kustomize build config/default | kubectl apply -f -\e[0m\n" - @cd config/manager && kustomize edit set image controller=$(IMG) - @kustomize build config/default | kubectl apply -f - - -# Undeploy controller from the k8s cluster -undeploy: FORCE install-kustomize - @printf "\e[1;36m>> kustomize build config/default | kubectl delete -f -\e[0m\n" - @kustomize build config/default | kubectl delete --ignore-not-found=true -f - - -# Install CRDs into the k8s cluster -deploy-crds: FORCE generate install-kustomize - @if [ -d config/crd ]; then \ - @printf "\e[1;36m>> kustomize build config/crd | kubectl apply -f -\e[0m\n"; \ - @kustomize build config/crd | kubectl apply -f -; \ - fi - -# Uninstall CRDs from the k8s cluster -undeploy-crds: FORCE install-kustomize - @if [ -d config/crd ]; then \ - @printf "\e[1;36m>> kustomize build config/crd | kubectl delete -f -\e[0m\n"; \ - @kustomize build config/crd | kubectl delete --ignore-not-found=true -f -; \ - fi - -# Create a Kind cluster for local development and testing. -kind-create: FORCE - @command -v kind >/dev/null 2>&1 || { \ - echo "Kind is not installed. Please install Kind manually."; \ - exit 1; \ - } - @kind get clusters | grep -q $(KIND_CLUSTER_NAME) || { \ - printf "\e[1;36m>> kind create cluster --name=$(KIND_CLUSTER_NAME)\e[0m\n"; \ - kind create cluster --name=$(KIND_CLUSTER_NAME); \ - } - -# Delete the Kind cluster created for local development and testing. -kind-delete: FORCE - @command -v kind >/dev/null 2>&1 || { \ - echo "Kind is not installed. Please install Kind manually."; \ - exit 1; \ - } - @printf "\e[1;36m>> kind delete cluster --name=$(KIND_CLUSTER_NAME)\e[0m\n" - @kind delete cluster --name=$(KIND_CLUSTER_NAME) - -tilt-up: FORCE kind-create - @command -v tilt >/dev/null 2>&1 || { \ - echo "Tilt is not installed. Please install Tilt manually."; \ - exit 1; \ - } - @printf "\e[1;36m>> tilt up --context kind-$(KIND_CLUSTER_NAME)\e[0m\n" - @tilt up --context kind-$(KIND_CLUSTER_NAME) - -# Generate manifests e.g. CRD, RBAC etc. -charts: FORCE generate - @printf "\e[1;36m>> kubebuilder edit --plugins=helm/v1-alpha\e[0m\n" - @kubebuilder edit --plugins=helm/v1-alpha - @rm -rf charts/network-operator && mv dist/chart charts/network-operator && rm -rf dist - -install-goimports: FORCE - @if ! hash goimports 2>/dev/null; then printf "\e[1;36m>> Installing goimports (this may take a while)...\e[0m\n"; go install golang.org/x/tools/cmd/goimports@latest; fi - -install-golangci-lint: FORCE - @if ! hash golangci-lint 2>/dev/null; then printf "\e[1;36m>> Installing golangci-lint (this may take a while)...\e[0m\n"; go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest; fi - -install-modernize: FORCE - @if ! hash modernize 2>/dev/null; then printf "\e[1;36m>> Installing modernize (this may take a while)...\e[0m\n"; go install golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest; fi - -install-shellcheck: FORCE - @if ! hash shellcheck 2>/dev/null; then printf "\e[1;36m>> Installing shellcheck...\e[0m\n"; SHELLCHECK_ARCH=$(shell uname -m); SHELLCHECK_OS=$(shell uname -s | tr '[:upper:]' '[:lower:]'); if [[ "$$SHELLCHECK_OS" == "darwin" ]]; then SHELLCHECK_OS=macos; fi; SHELLCHECK_VERSION="stable"; curl -sLo- "https://github.com/koalaman/shellcheck/releases/download/$$SHELLCHECK_VERSION/shellcheck-$$SHELLCHECK_VERSION.$$SHELLCHECK_OS.$$SHELLCHECK_ARCH.tar.xz" | tar -Jxf -; BIN=$$(go env GOBIN); if [[ -z $$BIN ]]; then BIN=$$(go env GOPATH)/bin; fi; install -Dm755 shellcheck-$$SHELLCHECK_VERSION/shellcheck -t "$$BIN"; rm -rf shellcheck-$$SHELLCHECK_VERSION; fi - -install-ginkgo: FORCE - @if ! hash ginkgo 2>/dev/null; then printf "\e[1;36m>> Installing ginkgo (this may take a while)...\e[0m\n"; go install github.com/onsi/ginkgo/v2/ginkgo@latest; fi - -install-go-licence-detector: FORCE - @if ! hash go-licence-detector 2>/dev/null; then printf "\e[1;36m>> Installing go-licence-detector (this may take a while)...\e[0m\n"; go install go.elastic.co/go-licence-detector@latest; fi - -install-addlicense: FORCE - @if ! hash addlicense 2>/dev/null; then printf "\e[1;36m>> Installing addlicense (this may take a while)...\e[0m\n"; go install github.com/google/addlicense@latest; fi - -prepare-static-check: FORCE install-golangci-lint install-modernize install-shellcheck install-ginkgo install-go-licence-detector install-addlicense - -install-controller-gen: FORCE - @if ! hash controller-gen 2>/dev/null; then printf "\e[1;36m>> Installing controller-gen (this may take a while)...\e[0m\n"; go install sigs.k8s.io/controller-tools/cmd/controller-gen@latest; fi - -install-setup-envtest: FORCE - @if ! hash setup-envtest 2>/dev/null; then printf "\e[1;36m>> Installing setup-envtest (this may take a while)...\e[0m\n"; go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest; fi - -GO_BUILDFLAGS = -GO_LDFLAGS = -GO_TESTENV = -GO_BUILDENV = CGO_ENABLED=0 - -# These definitions are overridable, e.g. to provide fixed version/commit values when -# no .git directory is present or to provide a fixed build date for reproducibility. -BININFO_VERSION ?= $(shell git describe --tags --always --abbrev=7) -BININFO_COMMIT_HASH ?= $(shell git rev-parse --verify HEAD) -BININFO_BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") - -build-all: build/network-operator - -build/network-operator: FORCE generate - env $(GO_BUILDENV) go build $(GO_BUILDFLAGS) -ldflags '-s -w -X github.com/sapcc/go-api-declarations/bininfo.binName=network-operator -X github.com/sapcc/go-api-declarations/bininfo.version=$(BININFO_VERSION) -X github.com/sapcc/go-api-declarations/bininfo.commit=$(BININFO_COMMIT_HASH) -X github.com/sapcc/go-api-declarations/bininfo.buildDate=$(BININFO_BUILD_DATE) $(GO_LDFLAGS)' -o build/network-operator ./cmd/manager - -DESTDIR = -ifeq ($(shell uname -s),Darwin) - PREFIX = /usr/local -else - PREFIX = /usr + go test ./test/e2e/ -v -ginkgo.v + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify + +.PHONY: add-license +add-license: addlicense ## Add license headers to all go files. + find . -name '*.go' -exec $(ADDLICENSE) -f hack/license-header.txt {} + + +.PHONY: check-license +check-license: addlicense ## Check that every file has a license header present. + find . -name '*.go' -exec $(ADDLICENSE) -check -c 'IronCore authors' {} + + +.PHONY: check +check: generate manifests add-license fmt lint test # Generate manifests, code, lint, add licenses, test + +##@ Build + +.PHONY: docs +docs: gen-crd-api-reference-docs ## Run go generate to generate API reference documentation. + $(GEN_CRD_API_REFERENCE_DOCS) -api-dir ./api/v1alpha1 -config ./hack/api-reference/config.json -template-dir ./hack/api-reference/template -out-file ./docs/api-reference/api.md + +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager cmd/manager/main.go + +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/manager/main.go + +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: docker-build-controller-manager + +.PHONY: docker-build-controller-manager +docker-build-controller-manager: ## Build controller-manager. + docker build --target manager -t ${CONTROLLER_IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + $(CONTAINER_TOOL) push ${CONTROLLER_IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name network-operator-builder + $(CONTAINER_TOOL) buildx use network-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${CONTROLLER_IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm network-operator-builder + rm Dockerfile.cross + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${CONTROLLER_IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml + +##@ Deployment + +ifndef ignore-not-found + ignore-not-found = false endif -install: FORCE build/network-operator - install -d -m 0755 "$(DESTDIR)$(PREFIX)/bin" - install -m 0755 build/network-operator "$(DESTDIR)$(PREFIX)/bin/network-operator" +.PHONY: install +install: manifests kustomize kubectl ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${CONTROLLER_IMG} + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + +.PHONY: e2e-deploy +e2e-deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${CONTROLLER_IMG} + $(KUSTOMIZE) build config/e2e-metrics-validation | $(KUBECTL) apply -f - + +.PHONY: undeploy +undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: helm +helm: manifests kubebuilder + $(KUBEBUILDER) edit --plugins=helm/v1-alpha + +##@ Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +CURL_RETRIES=3 + +## Tool Binaries +KUBECTL ?= $(LOCALBIN)/kubectl-$(ENVTEST_K8S_VERSION) +KUBECTL_BIN ?= $(LOCALBIN)/kubectl +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +ADDLICENSE ?= $(LOCALBIN)/addlicense +GEN_CRD_API_REFERENCE_DOCS ?= $(LOCALBIN)/gen-crd-api-reference-docs +KUBEBUILDER ?= $(LOCALBIN)/kubebuilder +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint +GOIMPORTS ?= $(LOCALBIN)/goimports + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.5.0 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 +#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) +ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) +ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d.%d",$$3, $$2}') +GOLANGCI_LINT_VERSION ?= v2.1 +GOIMPORTS_VERSION ?= v0.31.0 +GEN_CRD_API_REFERENCE_DOCS_VERSION ?= v0.3.0 +KUBEBUILDER_VERSION ?= v4.5.1 +ADDLICENSE_VERSION ?= v1.1.1 + +.PHONY: addlicense +addlicense: $(ADDLICENSE) ## Download addlicense locally if necessary. +$(ADDLICENSE): $(LOCALBIN) + $(call go-install-tool,$(ADDLICENSE),github.com/google/addlicense,$(ADDLICENSE_VERSION)) + +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): $(LOCALBIN) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + +.PHONY: kubectl +kubectl: $(KUBECTL) ## Download kubectl locally if necessary. +$(KUBECTL): $(LOCALBIN) + curl --retry $(CURL_RETRIES) -fsL https://dl.k8s.io/release/v$(ENVTEST_K8S_VERSION)/bin/$(GOOS)/$(GOARCH)/kubectl -o $(KUBECTL) + ln -sf "$(KUBECTL)" "$(KUBECTL_BIN)" + chmod +x "$(KUBECTL_BIN)" "$(KUBECTL)" + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: setup-envtest +setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. + @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." + @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ + echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ + exit 1; \ + } -# which packages to test with test runner -GO_TESTPKGS := $(shell go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.Dir}}{{end}}' ./... | grep -Ev '/test/e2e') -ifeq ($(GO_TESTPKGS),) -GO_TESTPKGS := ./... -endif -# which packages to measure coverage for -GO_COVERPKGS := $(shell go list ./... | grep -E '/internal') -# to get around weird Makefile syntax restrictions, we need variables containing nothing, a space and comma -null := -space := $(null) $(null) -comma := , - -check: FORCE static-check build/cover.html build-all - @printf "\e[1;32m>> All checks successful.\e[0m\n" - -generate: install-controller-gen - @printf "\e[1;36m>> controller-gen\e[0m\n" - @controller-gen crd rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases - @controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..." - -run-golangci-lint: FORCE install-golangci-lint - @printf "\e[1;36m>> golangci-lint\e[0m\n" - @golangci-lint config verify - @golangci-lint run - -run-modernize: FORCE install-modernize - @printf "\e[1;36m>> modernize\e[0m\n" - @modernize $(GO_TESTPKGS) - -run-shellcheck: FORCE install-shellcheck - @printf "\e[1;36m>> shellcheck\e[0m\n" - @find . -type f \( -name '*.bash' -o -name '*.ksh' -o -name '*.zsh' -o -name '*.sh' -o -name '*.shlib' \) -exec shellcheck {} + - -build/cover.out: FORCE install-ginkgo generate install-setup-envtest | build - @printf "\e[1;36m>> Running tests\e[0m\n" - KUBEBUILDER_ASSETS=$$(setup-envtest use 1.32 -p path) ginkgo run --randomize-all -output-dir=build $(GO_BUILDFLAGS) -ldflags '-s -w -X github.com/sapcc/go-api-declarations/bininfo.binName=network-operator -X github.com/sapcc/go-api-declarations/bininfo.version=$(BININFO_VERSION) -X github.com/sapcc/go-api-declarations/bininfo.commit=$(BININFO_COMMIT_HASH) -X github.com/sapcc/go-api-declarations/bininfo.buildDate=$(BININFO_BUILD_DATE) $(GO_LDFLAGS)' -covermode=count -coverpkg=$(subst $(space),$(comma),$(GO_COVERPKGS)) $(GO_TESTPKGS) - @mv build/coverprofile.out build/cover.out - -build/cover.html: build/cover.out - @printf "\e[1;36m>> go tool cover > build/cover.html\e[0m\n" - @go tool cover -html $< -o $@ - -check-addlicense: FORCE install-addlicense - @printf "\e[1;36m>> addlicense --check\e[0m\n" - @addlicense --check -- $(patsubst $(shell awk '$$1 == "module" {print $$2}' go.mod)%,.%/*.go,$(shell go list ./...)) - -check-reuse: FORCE - @printf "\e[1;36m>> reuse lint\e[0m\n" - @if ! reuse lint -q; then reuse lint; fi - -check-license-headers: FORCE check-addlicense check-reuse - -__static-check: FORCE run-shellcheck run-golangci-lint run-modernize check-dependency-licenses check-license-headers - -static-check: FORCE - @$(MAKE) --keep-going --no-print-directory __static-check - -build: - @mkdir $@ - -tidy-deps: FORCE - go mod tidy - go mod verify - -license-headers: FORCE install-addlicense - @printf "\e[1;36m>> addlicense (for license headers on source code files)\e[0m\n" - @printf "%s\0" $(patsubst $(shell awk '$$1 == "module" {print $$2}' go.mod)%,.%/*.go,$(shell go list ./...)) | $(XARGS) -0 -I{} bash -c 'year="$$(grep 'Copyright' {} | head -n1 | grep -E -o '"'"'[0-9]{4}(-[0-9]{4})?'"'"')"; if [[ -z "$$year" ]]; then year=$$(date +%Y); fi; gawk -i inplace '"'"'{if (display) {print} else {!/^\/\*/ && !/^\*/}}; {if (!display && $$0 ~ /^(package |$$)/) {display=1} else { }}'"'"' {}; addlicense -c "SAP SE or an SAP affiliate company" -s=only -y "$$year" -- {}; $(SED) -i '"'"'1s+// Copyright +// SPDX-FileCopyrightText: +'"'"' {}; ' - @printf "\e[1;36m>> reuse annotate (for license headers on other files)\e[0m\n" - @reuse lint -j | jq -r '.non_compliant.missing_licensing_info[]' | grep -vw vendor | $(XARGS) reuse annotate -c 'SAP SE or an SAP affiliate company' -l Apache-2.0 --skip-unrecognised - @printf "\e[1;36m>> reuse download --all\e[0m\n" - @reuse download --all - @printf "\e[1;35mPlease review the changes. If *.license files were generated, consider instructing go-makefile-maker to add overrides to REUSE.toml instead.\e[0m\n" - -check-dependency-licenses: FORCE install-go-licence-detector - @printf "\e[1;36m>> go-licence-detector\e[0m\n" - @go list -m -mod=readonly -json all | go-licence-detector -includeIndirect -rules .license-scan-rules.json -overrides .license-scan-overrides.jsonl - -goimports: FORCE install-goimports - @printf "\e[1;36m>> goimports -w -local https://github.com/ironcore-dev/network-operator\e[0m\n" - @goimports -w -local github.com/ironcore-dev/network-operator $(patsubst $(shell awk '$$1 == "module" {print $$2}' go.mod)%,.%/*.go,$(shell go list ./...)) - -modernize: FORCE install-modernize - @printf "\e[1;36m>> modernize -fix ./...\e[0m\n" - @modernize -fix ./... - -clean: FORCE - git clean -dxf build - -vars: FORCE - @printf "BININFO_BUILD_DATE=$(BININFO_BUILD_DATE)\n" - @printf "BININFO_COMMIT_HASH=$(BININFO_COMMIT_HASH)\n" - @printf "BININFO_VERSION=$(BININFO_VERSION)\n" - @printf "DESTDIR=$(DESTDIR)\n" - @printf "GO_BUILDENV=$(GO_BUILDENV)\n" - @printf "GO_BUILDFLAGS=$(GO_BUILDFLAGS)\n" - @printf "GO_COVERPKGS=$(GO_COVERPKGS)\n" - @printf "GO_LDFLAGS=$(GO_LDFLAGS)\n" - @printf "GO_TESTPKGS=$(GO_TESTPKGS)\n" - @printf "MAKE=$(MAKE)\n" - @printf "PREFIX=$(PREFIX)\n" - @printf "SED=$(SED)\n" - @printf "UNAME_S=$(UNAME_S)\n" - @printf "XARGS=$(XARGS)\n" -help: FORCE - @printf "\n" - @printf "\e[1mUsage:\e[0m\n" - @printf " make \e[36m\e[0m\n" - @printf "\n" - @printf "\e[1mGeneral\e[0m\n" - @printf " \e[36mvars\e[0m Display values of relevant Makefile variables.\n" - @printf " \e[36mhelp\e[0m Display this help.\n" - @printf "\n" - @printf "\e[1mPrepare\e[0m\n" - @printf " \e[36minstall-goimports\e[0m Install goimports required by goimports/static-check\n" - @printf " \e[36minstall-golangci-lint\e[0m Install golangci-lint required by run-golangci-lint/static-check\n" - @printf " \e[36minstall-modernize\e[0m Install modernize required by run-modernize/static-check\n" - @printf " \e[36minstall-shellcheck\e[0m Install shellcheck required by run-shellcheck/static-check\n" - @printf " \e[36minstall-ginkgo\e[0m Install ginkgo required when using it as test runner. This is used in CI before dropping privileges, you should probably install all the tools using your package manager\n" - @printf " \e[36minstall-go-licence-detector\e[0m Install-go-licence-detector required by check-dependency-licenses/static-check\n" - @printf " \e[36minstall-addlicense\e[0m Install addlicense required by check-license-headers/license-headers/static-check\n" - @printf " \e[36mprepare-static-check\e[0m Install any tools required by static-check. This is used in CI before dropping privileges, you should probably install all the tools using your package manager\n" - @printf " \e[36minstall-controller-gen\e[0m Install controller-gen required by static-check and build-all. This is used in CI before dropping privileges, you should probably install all the tools using your package manager\n" - @printf " \e[36minstall-setup-envtest\e[0m Install setup-envtest required by check. This is used in CI before dropping privileges, you should probably install all the tools using your package manager\n" - @printf "\n" - @printf "\e[1mBuild\e[0m\n" - @printf " \e[36mbuild-all\e[0m Build all binaries.\n" - @printf " \e[36mbuild/network-operator\e[0m Build network-operator.\n" - @printf " \e[36minstall\e[0m Install all binaries. This option understands the conventional 'DESTDIR' and 'PREFIX' environment variables for choosing install locations.\n" - @printf "\n" - @printf "\e[1mTest\e[0m\n" - @printf " \e[36mcheck\e[0m Run the test suite (unit tests and golangci-lint).\n" - @printf " \e[36mgenerate\e[0m Generate code for Kubernetes CRDs and deepcopy.\n" - @printf " \e[36mrun-golangci-lint\e[0m Install and run golangci-lint. Installing is used in CI, but you should probably install golangci-lint using your package manager.\n" - @printf " \e[36mrun-modernize\e[0m Install and run modernize. Installing is used in CI, but you should probably install modernize using your package manager.\n" - @printf " \e[36mrun-shellcheck\e[0m Install and run shellcheck. Installing is used in CI, but you should probably install shellcheck using your package manager.\n" - @printf " \e[36mbuild/cover.out\e[0m Run tests and generate coverage report.\n" - @printf " \e[36mbuild/cover.html\e[0m Generate an HTML file with source code annotations from the coverage report.\n" - @printf " \e[36mcheck-addlicense\e[0m Check license headers in all non-vendored .go files with addlicense.\n" - @printf " \e[36mcheck-reuse\e[0m Check reuse compliance\n" - @printf " \e[36mcheck-license-headers\e[0m Run static code checks\n" - @printf " \e[36mstatic-check\e[0m Run static code checks\n" - @printf "\n" - @printf "\e[1mDevelopment\e[0m\n" - @printf " \e[36mtidy-deps\e[0m Run go mod tidy and go mod verify.\n" - @printf " \e[36mlicense-headers\e[0m Add (or overwrite) license headers on all non-vendored source code files.\n" - @printf " \e[36mcheck-dependency-licenses\e[0m Check all dependency licenses using go-licence-detector.\n" - @printf " \e[36mgoimports\e[0m Run goimports on all non-vendored .go files\n" - @printf " \e[36mmodernize\e[0m Run modernize on all non-vendored .go files\n" - @printf " \e[36mclean\e[0m Run git clean.\n" - -.PHONY: FORCE +.PHONY: envtest +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. +$(ENVTEST): $(LOCALBIN) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,${GOLANGCI_LINT_VERSION}) + +.PHONY: goimports +goimports: $(GOIMPORTS) ## Download goimports locally if necessary. +$(GOIMPORTS): $(LOCALBIN) + $(call go-install-tool,$(GOIMPORTS),golang.org/x/tools/cmd/goimports,$(GOIMPORTS_VERSION)) + +.PHONY: gen-crd-api-reference-docs +gen-crd-api-reference-docs: $(GEN_CRD_API_REFERENCE_DOCS) ## Download gen-crd-api-reference-docs locally if necessary. +$(GEN_CRD_API_REFERENCE_DOCS): $(LOCALBIN) + $(call go-install-tool,$(GEN_CRD_API_REFERENCE_DOCS),github.com/ahmetb/gen-crd-api-reference-docs,$(GEN_CRD_API_REFERENCE_DOCS_VERSION)) + +.PHONY: kubebuilder +kubebuilder: $(KUBEBUILDER) ## Download kubebuilder locally if necessary. +$(KUBEBUILDER): $(LOCALBIN) + $(call go-install-tool,$(KUBEBUILDER),sigs.k8s.io/kubebuilder/v4,$(KUBEBUILDER_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef + +## -------------------------------------- +## Tilt / Kind +## -------------------------------------- + +KIND_CLUSTER_NAME ?= network-operator + +.PHONY: kind-create +kind-create: $(ENVTEST) ## create kind cluster if needed + ./hack/kind-with-registry.sh + +.PHONY: kind-delete +kind-delete: ## Destroys the kind cluster. + kind delete cluster --name=$(KIND_CLUSTER_NAME) + docker stop kind-registry && docker rm kind-registry + +.PHONY: tilt-up +tilt-up: $(ENVTEST) $(KUSTOMIZE) kind-create ## start tilt and build kind cluster if needed + EXP_CLUSTER_RESOURCE_SET=true tilt up diff --git a/dist/chart/.helmignore b/dist/chart/.helmignore new file mode 100644 index 00000000..7d92f7fb --- /dev/null +++ b/dist/chart/.helmignore @@ -0,0 +1,25 @@ +# Patterns to ignore when building Helm packages. +# Operating system files +.DS_Store + +# Version control directories +.git/ +.gitignore +.bzr/ +.hg/ +.hgignore +.svn/ + +# Backup and temporary files +*.swp +*.tmp +*.bak +*.orig +*~ + +# IDE and editor-related files +.idea/ +.vscode/ + +# Helm chart artifacts +dist/chart/*.tgz diff --git a/dist/chart/Chart.yaml b/dist/chart/Chart.yaml new file mode 100644 index 00000000..5eb1a44f --- /dev/null +++ b/dist/chart/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: network-operator +description: A Helm chart to distribute the project network-operator +type: application +version: 0.1.0 +appVersion: "0.1.0" +icon: "https://example.com/icon.png" diff --git a/dist/chart/templates/_helpers.tpl b/dist/chart/templates/_helpers.tpl new file mode 100644 index 00000000..6ec3c83c --- /dev/null +++ b/dist/chart/templates/_helpers.tpl @@ -0,0 +1,50 @@ +{{- define "chart.name" -}} +{{- if .Chart }} + {{- if .Chart.Name }} + {{- .Chart.Name | trunc 63 | trimSuffix "-" }} + {{- else if .Values.nameOverride }} + {{ .Values.nameOverride | trunc 63 | trimSuffix "-" }} + {{- else }} + network-operator + {{- end }} +{{- else }} + network-operator +{{- end }} +{{- end }} + + +{{- define "chart.labels" -}} +{{- if .Chart.AppVersion -}} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +{{- if .Chart.Version }} +helm.sh/chart: {{ .Chart.Version | quote }} +{{- end }} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + + +{{- define "chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + + +{{- define "chart.hasMutatingWebhooks" -}} +{{- $hasMutating := false }} +{{- range . }} + {{- if eq .type "mutating" }} + $hasMutating = true }}{{- end }} +{{- end }} +{{ $hasMutating }}}}{{- end }} + + +{{- define "chart.hasValidatingWebhooks" -}} +{{- $hasValidating := false }} +{{- range . }} + {{- if eq .type "validating" }} + $hasValidating = true }}{{- end }} +{{- end }} +{{ $hasValidating }}}}{{- end }} diff --git a/dist/chart/templates/certmanager/certificate.yaml b/dist/chart/templates/certmanager/certificate.yaml new file mode 100644 index 00000000..c63b8297 --- /dev/null +++ b/dist/chart/templates/certmanager/certificate.yaml @@ -0,0 +1,60 @@ +{{- if .Values.certmanager.enable }} +# Self-signed Issuer +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: selfsigned-issuer + namespace: {{ .Release.Namespace }} +spec: + selfSigned: {} +{{- if .Values.webhook.enable }} +--- +# Certificate for the webhook +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + {{- if .Values.crd.keep }} + "helm.sh/resource-policy": keep + {{- end }} + name: serving-cert + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + dnsNames: + - network-operator.{{ .Release.Namespace }}.svc + - network-operator.{{ .Release.Namespace }}.svc.cluster.local + - network-operator-webhook-service.{{ .Release.Namespace }}.svc + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert +{{- end }} +{{- if .Values.metrics.enable }} +--- +# Certificate for the metrics +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + {{- if .Values.crd.keep }} + "helm.sh/resource-policy": keep + {{- end }} + labels: + {{- include "chart.labels" . | nindent 4 }} + name: metrics-certs + namespace: {{ .Release.Namespace }} +spec: + dnsNames: + - network-operator.{{ .Release.Namespace }}.svc + - network-operator.{{ .Release.Namespace }}.svc.cluster.local + - network-operator-metrics-service.{{ .Release.Namespace }}.svc + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert +{{- end }} +{{- end }} diff --git a/dist/chart/templates/manager/manager.yaml b/dist/chart/templates/manager/manager.yaml new file mode 100644 index 00000000..15cf018e --- /dev/null +++ b/dist/chart/templates/manager/manager.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: network-operator-controller-manager + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} + control-plane: controller-manager +spec: + replicas: {{ .Values.controllerManager.replicas }} + selector: + matchLabels: + {{- include "chart.selectorLabels" . | nindent 6 }} + control-plane: controller-manager + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + {{- include "chart.labels" . | nindent 8 }} + control-plane: controller-manager + {{- if and .Values.controllerManager.pod .Values.controllerManager.pod.labels }} + {{- range $key, $value := .Values.controllerManager.pod.labels }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + containers: + - name: manager + args: + {{- range .Values.controllerManager.container.args }} + - {{ . }} + {{- end }} + command: + - /manager + image: {{ .Values.controllerManager.container.image.repository }}:{{ .Values.controllerManager.container.image.tag }} + {{- if .Values.controllerManager.container.env }} + env: + {{- range $key, $value := .Values.controllerManager.container.env }} + - name: {{ $key }} + value: {{ $value }} + {{- end }} + {{- end }} + livenessProbe: + {{- toYaml .Values.controllerManager.container.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.controllerManager.container.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.controllerManager.container.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.controllerManager.container.securityContext | nindent 12 }} + {{- if and .Values.certmanager.enable (or .Values.webhook.enable .Values.metrics.enable) }} + volumeMounts: + {{- if and .Values.metrics.enable .Values.certmanager.enable }} + - name: metrics-certs + mountPath: /tmp/k8s-metrics-server/metrics-certs + readOnly: true + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.controllerManager.securityContext | nindent 8 }} + serviceAccountName: {{ .Values.controllerManager.serviceAccountName }} + terminationGracePeriodSeconds: {{ .Values.controllerManager.terminationGracePeriodSeconds }} + {{- if and .Values.certmanager.enable (or .Values.webhook.enable .Values.metrics.enable) }} + volumes: + {{- if and .Values.metrics.enable .Values.certmanager.enable }} + - name: metrics-certs + secret: + secretName: metrics-server-cert + {{- end }} + {{- end }} diff --git a/dist/chart/templates/metrics/metrics-service.yaml b/dist/chart/templates/metrics/metrics-service.yaml new file mode 100644 index 00000000..9c0db664 --- /dev/null +++ b/dist/chart/templates/metrics/metrics-service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.metrics.enable }} +apiVersion: v1 +kind: Service +metadata: + name: network-operator-controller-manager-metrics-service + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + ports: + - port: 8443 + targetPort: 8443 + protocol: TCP + name: https + selector: + control-plane: controller-manager +{{- end }} diff --git a/dist/chart/templates/network-policy/allow-metrics-traffic.yaml b/dist/chart/templates/network-policy/allow-metrics-traffic.yaml new file mode 100755 index 00000000..eebe1f14 --- /dev/null +++ b/dist/chart/templates/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,28 @@ +{{- if .Values.networkPolicy.enable }} +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gather data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: allow-metrics-traffic + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: network-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP +{{- end -}} diff --git a/dist/chart/templates/prometheus/monitor.yaml b/dist/chart/templates/prometheus/monitor.yaml new file mode 100644 index 00000000..aaa848a1 --- /dev/null +++ b/dist/chart/templates/prometheus/monitor.yaml @@ -0,0 +1,39 @@ +# To integrate with Prometheus. +{{- if .Values.prometheus.enable }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: network-operator-controller-manager-metrics-monitor + namespace: {{ .Release.Namespace }} +spec: + endpoints: + - path: /metrics + port: https + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + {{- if .Values.certmanager.enable }} + serverName: network-operator-controller-manager-metrics-service.{{ .Release.Namespace }}.svc + # Apply secure TLS configuration with cert-manager + insecureSkipVerify: false + ca: + secret: + name: metrics-server-cert + key: ca.crt + cert: + secret: + name: metrics-server-cert + key: tls.crt + keySecret: + name: metrics-server-cert + key: tls.key + {{- else }} + # Development/Test mode (insecure configuration) + insecureSkipVerify: true + {{- end }} + selector: + matchLabels: + control-plane: controller-manager +{{- end }} diff --git a/dist/chart/templates/rbac/leader_election_role.yaml b/dist/chart/templates/rbac/leader_election_role.yaml new file mode 100755 index 00000000..9b5bccc2 --- /dev/null +++ b/dist/chart/templates/rbac/leader_election_role.yaml @@ -0,0 +1,42 @@ +{{- if .Values.rbac.enable }} +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} + name: network-operator-leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- end -}} diff --git a/dist/chart/templates/rbac/leader_election_role_binding.yaml b/dist/chart/templates/rbac/leader_election_role_binding.yaml new file mode 100755 index 00000000..12254217 --- /dev/null +++ b/dist/chart/templates/rbac/leader_election_role_binding.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbac.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} + name: network-operator-leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: network-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/templates/rbac/metrics_auth_role.yaml b/dist/chart/templates/rbac/metrics_auth_role.yaml new file mode 100755 index 00000000..55191de8 --- /dev/null +++ b/dist/chart/templates/rbac/metrics_auth_role.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.rbac.enable .Values.metrics.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: network-operator-metrics-auth-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +{{- end -}} diff --git a/dist/chart/templates/rbac/metrics_auth_role_binding.yaml b/dist/chart/templates/rbac/metrics_auth_role_binding.yaml new file mode 100755 index 00000000..04094347 --- /dev/null +++ b/dist/chart/templates/rbac/metrics_auth_role_binding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.rbac.enable .Values.metrics.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: network-operator-metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: network-operator-metrics-auth-role +subjects: +- kind: ServiceAccount + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/templates/rbac/metrics_reader_role.yaml b/dist/chart/templates/rbac/metrics_reader_role.yaml new file mode 100755 index 00000000..91d0f252 --- /dev/null +++ b/dist/chart/templates/rbac/metrics_reader_role.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.rbac.enable .Values.metrics.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: network-operator-metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/role.yaml b/dist/chart/templates/rbac/role.yaml new file mode 100755 index 00000000..2f65d01d --- /dev/null +++ b/dist/chart/templates/rbac/role.yaml @@ -0,0 +1,12 @@ +{{- if .Values.rbac.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: network-operator-manager-role +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] +{{- end -}} diff --git a/dist/chart/templates/rbac/role_binding.yaml b/dist/chart/templates/rbac/role_binding.yaml new file mode 100755 index 00000000..5787fe37 --- /dev/null +++ b/dist/chart/templates/rbac/role_binding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: network-operator-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: network-operator-manager-role +subjects: +- kind: ServiceAccount + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/templates/rbac/service_account.yaml b/dist/chart/templates/rbac/service_account.yaml new file mode 100755 index 00000000..93e0a323 --- /dev/null +++ b/dist/chart/templates/rbac/service_account.yaml @@ -0,0 +1,15 @@ +{{- if .Values.rbac.enable }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- if and .Values.controllerManager.serviceAccount .Values.controllerManager.serviceAccount.annotations }} + annotations: + {{- range $key, $value := .Values.controllerManager.serviceAccount.annotations }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/values.yaml b/dist/chart/values.yaml new file mode 100644 index 00000000..9ff6bb35 --- /dev/null +++ b/dist/chart/values.yaml @@ -0,0 +1,76 @@ +# [MANAGER]: Manager Deployment Configurations +controllerManager: + replicas: 1 + container: + image: + repository: controller + tag: latest + args: + - "--leader-elect" + - "--metrics-bind-address=:8443" + - "--health-probe-bind-address=:8081" + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + livenessProbe: + initialDelaySeconds: 15 + periodSeconds: 20 + httpGet: + path: /healthz + port: 8081 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 10 + httpGet: + path: /readyz + port: 8081 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + terminationGracePeriodSeconds: 10 + serviceAccountName: network-operator-controller-manager + +# [RBAC]: To enable RBAC (Permissions) configurations +rbac: + enable: true + +# [CRDs]: To enable the CRDs +crd: + # This option determines whether the CRDs are included + # in the installation process. + enable: true + + # Enabling this option adds the "helm.sh/resource-policy": keep + # annotation to the CRD, ensuring it remains installed even when + # the Helm release is uninstalled. + # NOTE: Removing the CRDs will also remove all cert-manager CR(s) + # (Certificates, Issuers, ...) due to garbage collection. + keep: true + +# [METRICS]: Set to true to generate manifests for exporting metrics. +# To disable metrics export set false, and ensure that the +# ControllerManager argument "--metrics-bind-address=:8443" is removed. +metrics: + enable: true + +# [PROMETHEUS]: To enable a ServiceMonitor to export metrics to Prometheus set true +prometheus: + enable: false + +# [CERT-MANAGER]: To enable cert-manager injection to webhooks set true +certmanager: + enable: false + +# [NETWORK POLICIES]: To enable NetworkPolicies set true +networkPolicy: + enable: false diff --git a/hack/api-reference/config.json b/hack/api-reference/config.json new file mode 100644 index 00000000..d3133f9f --- /dev/null +++ b/hack/api-reference/config.json @@ -0,0 +1,32 @@ +{ + "hideMemberFields": [ + "TypeMeta" + ], + "hideTypePatterns": [ + "ParseError$", + "List$" + ], + "externalPackages": [ + { + "typeMatchPrefix": "^net/netip", + "docsURLTemplate": "https://pkg.go.dev/net/netip#{{.TypeIdentifier}}" + }, + { + "typeMatchPrefix": "^k8s\\.io/apimachinery/pkg/api/resource", + "docsURLTemplate": "https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#{{.TypeIdentifier}}" + }, + { + "typeMatchPrefix": "^k8s\\.io/apimachinery/pkg/types", + "docsURLTemplate": "https://pkg.go.dev/k8s.io/apimachinery/pkg/types#{{.TypeIdentifier}}" + }, + { + "typeMatchPrefix": "^k8s\\.io/(api|apimachinery/pkg/apis)/", + "docsURLTemplate": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#{{lower .TypeIdentifier}}-{{arrIndex .PackageSegments -1}}-{{arrIndex .PackageSegments -2}}" + } + ], + "typeDisplayNamePrefixOverrides": { + "k8s.io/api/": "Kubernetes ", + "k8s.io/apimachinery/pkg/apis/": "Kubernetes " + }, + "markdownDisabled": false +} diff --git a/hack/api-reference/template/members.tpl b/hack/api-reference/template/members.tpl new file mode 100644 index 00000000..a529c671 --- /dev/null +++ b/hack/api-reference/template/members.tpl @@ -0,0 +1,48 @@ +{{ define "members" }} + +{{ range .Members }} +{{ if not (hiddenMember .)}} + + + {{ fieldName . }}
+ + {{ if linkForType .Type }} + + {{ typeDisplayName .Type }} + + {{ else }} + {{ typeDisplayName .Type }} + {{ end }} + + + + {{ if fieldEmbedded . }} +

+ (Members of {{ fieldName . }} are embedded into this type.) +

+ {{ end}} + + {{ if isOptionalMember .}} + (Optional) + {{ end }} + + {{ safe (renderComments .CommentLines) }} + + {{ if and (eq (.Type.Name.Name) "ObjectMeta") }} + Refer to the Kubernetes API documentation for the fields of the + metadata field. + {{ end }} + + {{ if or (eq (fieldName .) "spec") }} +
+
+ + {{ template "members" .Type }} +
+ {{ end }} + + +{{ end }} +{{ end }} + +{{ end }} diff --git a/hack/api-reference/template/pkg.tpl b/hack/api-reference/template/pkg.tpl new file mode 100644 index 00000000..14eae298 --- /dev/null +++ b/hack/api-reference/template/pkg.tpl @@ -0,0 +1,48 @@ +{{ define "packages" }} + +{{ with .packages}} +

Packages:

+ +{{ end}} + +{{ range .packages }} +

+ {{- packageDisplayName . -}} +

+ + {{ with (index .GoPackages 0 )}} + {{ with .DocComments }} +
+ {{ safe (renderComments .) }} +
+ {{ end }} + {{ end }} + + Resource Types: +
    + {{- range (visibleTypes (sortedTypes .Types)) -}} + {{ if isExportedType . -}} +
  • + {{ typeDisplayName . }} +
  • + {{- end }} + {{- end -}} +
+ + {{ range (visibleTypes (sortedTypes .Types))}} + {{ template "type" . }} + {{ end }} +
+{{ end }} + +

+ Generated with gen-crd-api-reference-docs +

+ +{{ end }} diff --git a/hack/api-reference/template/type.tpl b/hack/api-reference/template/type.tpl new file mode 100644 index 00000000..8f0d66b8 --- /dev/null +++ b/hack/api-reference/template/type.tpl @@ -0,0 +1,81 @@ +{{ define "type" }} + +

+ {{- .Name.Name }} + {{ if eq .Kind "Alias" }}({{.Underlying}} alias){{ end -}} +

+{{ with (typeReferences .) }} +

+ (Appears on: + {{- $prev := "" -}} + {{- range . -}} + {{- if $prev -}}, {{ end -}} + {{- $prev = . -}} + {{ typeDisplayName . }} + {{- end -}} + ) +

+{{ end }} + +
+ {{ safe (renderComments .CommentLines) }} +
+ +{{ with (constantsOfType .) }} + + + + + + + + + {{- range . -}} + + {{- /* + renderComments implicitly creates a

element, so we + add one to the display name as well to make the contents + of the two cells align evenly. + */ -}} +

+ + + {{- end -}} + +
ValueDescription

{{ typeDisplayName . }}

{{ safe (renderComments .CommentLines) }}
+{{ end }} + +{{ if .Members }} + + + + + + + + + {{ if isExportedType . }} + + + + + + + + + {{ end }} + {{ template "members" .}} + +
FieldDescription
+ apiVersion
+ string
+ + {{apiGroup .}} + +
+ kind
+ string +
{{.Name.Name}}
+{{ end }} + +{{ end }} diff --git a/hack/kind-with-registry.sh b/hack/kind-with-registry.sh new file mode 100755 index 00000000..4b29ea9e --- /dev/null +++ b/hack/kind-with-registry.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +#// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +#// SPDX-License-Identifier: Apache-2.0 + +set -o errexit +set -o nounset +set -o pipefail + + +REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +KUBECTL=$REPO_ROOT/bin/kubectl + +# desired kind cluster name; default is "metal" +KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-metal}" + +if [[ "$(kind get clusters)" =~ .*"${KIND_CLUSTER_NAME}".* ]]; then + echo "cluster already exists, moving on" + exit 0 +fi + +reg_name='kind-registry' +reg_port="${KIND_REGISTRY_PORT:-5000}" + +# create registry container unless it already exists +running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" +if [ "${running}" != 'true' ]; then + docker run -d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" registry:2 +fi + +# create a cluster with the local registry enabled in containerd +cat < /dev/null && pwd ) +export TERM="xterm-256color" + +bold="$(tput bold)" +red="$(tput setaf 1)" +green="$(tput setaf 2)" +normal="$(tput sgr0)" + +for kustomization in "$BASEDIR"/../config/**/kustomization.yaml; do + path="$(dirname "$kustomization")" + dir="$(realpath --relative-to "$BASEDIR"/.. "$path")" + echo "${bold}Validating $dir${normal}" + if ! kustomize_output="$(kustomize build "$path" 2>&1)"; then + echo "${red}Kustomize build $dir failed:" + echo "$kustomize_output" + exit 1 + fi + echo "${green}Successfully validated $dir${normal}" +done