From 756336e3126f68d3d443403139d44c1a9130f659 Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Wed, 20 Aug 2025 13:10:35 +0200 Subject: [PATCH 1/5] Refactor Makefile, GH workflows and other project housekeeping tasks --- .github/workflows/check-codegen.yml | 39 + .github/workflows/checks.yaml | 65 - .github/workflows/ci.yaml | 56 - .github/workflows/goreleaser.yaml | 10 +- .github/workflows/kustomize-validation.yml | 24 + .github/workflows/lint.yml | 26 + .github/workflows/publish-chart.yaml | 85 + .github/workflows/publish-docker.yml | 84 + .github/workflows/reuse.yaml | 6 +- .github/workflows/stale.yaml | 32 - .github/workflows/test-chart.yml | 111 +- .github/workflows/test-e2e.yml | 51 +- .github/workflows/test.yml | 26 + .golangci.yaml | 197 -- .golangci.yml | 93 + Dockerfile | 58 +- Makefile | 655 +++-- Makefile.maker.yaml | 182 -- api/v1alpha1/device_types.go | 10 +- api/v1alpha1/doc.go | 7 + api/v1alpha1/zz_generated.deepcopy.go | 2 +- cmd/main.go | 21 +- config/manager/manager.yaml | 71 +- .../network-policy/allow-metrics-traffic.yaml | 6 +- dist/chart/.helmignore | 25 + dist/chart/Chart.yaml | 7 + dist/chart/templates/_helpers.tpl | 50 + .../templates/certmanager/certificate.yaml | 60 + .../crd/networking.cloud.sap_devices.yaml | 777 ++++++ .../crd/networking.cloud.sap_interfaces.yaml | 241 ++ dist/chart/templates/manager/manager.yaml | 71 + .../templates/metrics/metrics-service.yaml | 17 + .../network-policy/allow-metrics-traffic.yaml | 28 + dist/chart/templates/prometheus/monitor.yaml | 39 + .../templates/rbac/device_admin_role.yaml | 28 + .../templates/rbac/device_editor_role.yaml | 34 + .../templates/rbac/device_viewer_role.yaml | 30 + .../templates/rbac/interface_admin_role.yaml | 28 + .../templates/rbac/interface_editor_role.yaml | 34 + .../templates/rbac/interface_viewer_role.yaml | 30 + .../templates/rbac/leader_election_role.yaml | 42 + .../rbac/leader_election_role_binding.yaml | 17 + .../templates/rbac/metrics_auth_role.yaml | 21 + .../rbac/metrics_auth_role_binding.yaml | 16 + .../templates/rbac/metrics_reader_role.yaml | 13 + dist/chart/templates/rbac/role.yaml | 63 + dist/chart/templates/rbac/role_binding.yaml | 16 + .../chart/templates/rbac/service_account.yaml | 15 + dist/chart/values.yaml | 76 + docs/api-reference/api.md | 2166 +++++++++++++++++ go.mod | 2 - go.sum | 6 - hack/api-reference/config.json | 32 + hack/api-reference/template/members.tpl | 48 + hack/api-reference/template/pkg.tpl | 48 + hack/api-reference/template/type.tpl | 81 + hack/license-header.txt | 2 + hack/validate-kustomize.sh | 23 + .../provider/cisco/nxos/api/nxapi_test.go | 18 +- internal/provider/cisco/nxos/provider.go | 7 +- test/e2e/e2e_suite_test.go | 64 +- test/e2e/e2e_test.go | 117 +- test/gnmi/main.go | 3 + test/{util => utils}/util.go | 119 +- 64 files changed, 5173 insertions(+), 1258 deletions(-) create mode 100644 .github/workflows/check-codegen.yml delete mode 100644 .github/workflows/checks.yaml delete mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/kustomize-validation.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/publish-chart.yaml create mode 100644 .github/workflows/publish-docker.yml delete mode 100644 .github/workflows/stale.yaml create mode 100644 .github/workflows/test.yml delete mode 100644 .golangci.yaml create mode 100644 .golangci.yml delete mode 100644 Makefile.maker.yaml create mode 100644 api/v1alpha1/doc.go create mode 100644 dist/chart/.helmignore create mode 100644 dist/chart/Chart.yaml create mode 100644 dist/chart/templates/_helpers.tpl create mode 100644 dist/chart/templates/certmanager/certificate.yaml create mode 100755 dist/chart/templates/crd/networking.cloud.sap_devices.yaml create mode 100755 dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml create mode 100644 dist/chart/templates/manager/manager.yaml create mode 100644 dist/chart/templates/metrics/metrics-service.yaml create mode 100755 dist/chart/templates/network-policy/allow-metrics-traffic.yaml create mode 100644 dist/chart/templates/prometheus/monitor.yaml create mode 100755 dist/chart/templates/rbac/device_admin_role.yaml create mode 100755 dist/chart/templates/rbac/device_editor_role.yaml create mode 100755 dist/chart/templates/rbac/device_viewer_role.yaml create mode 100755 dist/chart/templates/rbac/interface_admin_role.yaml create mode 100755 dist/chart/templates/rbac/interface_editor_role.yaml create mode 100755 dist/chart/templates/rbac/interface_viewer_role.yaml create mode 100755 dist/chart/templates/rbac/leader_election_role.yaml create mode 100755 dist/chart/templates/rbac/leader_election_role_binding.yaml create mode 100755 dist/chart/templates/rbac/metrics_auth_role.yaml create mode 100755 dist/chart/templates/rbac/metrics_auth_role_binding.yaml create mode 100755 dist/chart/templates/rbac/metrics_reader_role.yaml create mode 100755 dist/chart/templates/rbac/role.yaml create mode 100755 dist/chart/templates/rbac/role_binding.yaml create mode 100755 dist/chart/templates/rbac/service_account.yaml create mode 100644 dist/chart/values.yaml create mode 100644 docs/api-reference/api.md create mode 100644 hack/api-reference/config.json create mode 100644 hack/api-reference/template/members.tpl create mode 100644 hack/api-reference/template/pkg.tpl create mode 100644 hack/api-reference/template/type.tpl create mode 100644 hack/license-header.txt create mode 100755 hack/validate-kustomize.sh rename test/{util => utils}/util.go (64%) diff --git a/.github/workflows/check-codegen.yml b/.github/workflows/check-codegen.yml new file mode 100644 index 00000000..e501731e --- /dev/null +++ b/.github/workflows/check-codegen.yml @@ -0,0 +1,39 @@ +name: Check Codegen + +on: + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + check-codegen: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - 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: Run fmt + run: make fmt + + - 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 62f4ca0f..00000000 --- a/.github/workflows/checks.yaml +++ /dev/null @@ -1,65 +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.25.1 - - name: Cache golangci-lint analysis - uses: actions/cache@v4 - with: - path: ~/.cache/golangci-lint - key: ${{ runner.os }}-golangci-lint-${{ hashFiles('go.sum', '.golangci.yaml') }} - restore-keys: | - ${{ runner.os }}-golangci-lint- - - name: Run prepare make target - run: make generate - - name: Run golangci-lint - run: | - # FIXME: Exclude cisco nx-os provider from golangci-lint as it includes - # a lot of generated code that will exceed the runners's constraints. - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0 - go list -f '{{.Dir}}' ./... | grep -v nxos/ | xargs $(go env GOPATH)/bin/golangci-lint run - - 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 e1d61e74..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.25.1 - - 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.25.1 - - 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/goreleaser.yaml b/.github/workflows/goreleaser.yaml index 8daecb73..b8baf0f6 100644 --- a/.github/workflows/goreleaser.yaml +++ b/.github/workflows/goreleaser.yaml @@ -1,11 +1,3 @@ -################################################################################ -# 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: @@ -16,7 +8,7 @@ permissions: packages: write jobs: release: - name: goreleaser + name: Run on Ubuntu runs-on: ubuntu-latest steps: - name: Check out code diff --git a/.github/workflows/kustomize-validation.yml b/.github/workflows/kustomize-validation.yml new file mode 100644 index 00000000..9c2c40b1 --- /dev/null +++ b/.github/workflows/kustomize-validation.yml @@ -0,0 +1,24 @@ +name: Kustomize Validation + +on: + pull_request: + types: [ assigned, opened, synchronize, reopened ] + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + kustomize-validation: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v5 + + - name: Install Kustomize + run: | + make kustomize + mv ./bin/kustomize /usr/local/bin + + - run: | + ./hack/validate-kustomize.sh diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..e97024d1 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: Lint + +on: + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + lint: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v5 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run linter + run: | + make golangci-lint + cp ./bin/golangci-lint /usr/local/bin/ + go list -f '{{.Dir}}' ./... | grep -v nxos/ | xargs golangci-lint run diff --git a/.github/workflows/publish-chart.yaml b/.github/workflows/publish-chart.yaml new file mode 100644 index 00000000..ca9ae2ca --- /dev/null +++ b/.github/workflows/publish-chart.yaml @@ -0,0 +1,85 @@ +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: + name: Run on Ubuntu + 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@v5 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: v3.17.0 + + - 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: | + make kustomize + mv ./bin/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..566d0eaf --- /dev/null +++ b/.github/workflows/publish-docker.yml @@ -0,0 +1,84 @@ +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: + name: Run on Ubuntu + 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@v5 + + - 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 + + - name: Set up Docker Buildx + timeout-minutes: 5 + uses: docker/setup-buildx-action@v3 + with: + version: latest + + - 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/reuse.yaml b/.github/workflows/reuse.yaml index 7597d554..4d5022b7 100644 --- a/.github/workflows/reuse.yaml +++ b/.github/workflows/reuse.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors -# SPDX-License-Identifier: Apache-2.0 - name: REUSE Compliance on: push: @@ -14,9 +11,10 @@ permissions: contents: read jobs: test: - name: Check + name: Run on Ubuntu runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: REUSE Compliance Check uses: fsfe/reuse-action@v5 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 d41d461f..0fc2590a 100644 --- a/.github/workflows/test-chart.yml +++ b/.github/workflows/test-chart.yml @@ -1,95 +1,62 @@ -# 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 + name: Run on Ubuntu runs-on: ubuntu-latest steps: - - name: Check out code - uses: actions/checkout@v4 - - name: Set up Go + - name: Clone the code + uses: actions/checkout@v5 + + - name: Setup Go uses: actions/setup-go@v5 with: - check-latest: true - go-version: 1.25.1 - - 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 + + - uses: azure/setup-helm@v4.3.0 + with: + version: 'v3.17.0' + + - name: Lint Helm Chart 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 + helm lint ./dist/chart + + - 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 }} + cluster_name: 'kind' + - name: Prepare network-operator run: | - go mod tidy + go mod download make docker-build IMG=network-operator:v0.1.0 kind load docker-image network-operator:v0.1.0 - - name: Install Helm + + - name: Install cert-manager via 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 + 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: | - 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 + 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 dependency build ./charts/network-operator - helm install network-operator ./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 network-operator --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 + helm status my-release --namespace network-operator-system diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index a9496a9f..030b0ace 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -1,50 +1,31 @@ -# 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 - uses: actions/checkout@v4 - - name: Set up Go + - name: Clone the code + uses: actions/checkout@v5 + + - name: Setup Go uses: actions/setup-go@v5 with: - check-latest: true - go-version: 1.25.1 - - 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 + go-version-file: go.mod + + - 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 + install_only: true + + - 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..7dfc4938 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Tests + +on: + pull_request: + types: [ assigned, opened, synchronize, reopened ] + paths-ignore: + - 'docs/**' + - '**/*.md' + +jobs: + test: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v5 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Running Tests + run: | + go mod tidy + make test diff --git a/.golangci.yaml b/.golangci.yaml deleted file mode 100644 index 3e2b2b37..00000000 --- a/.golangci.yaml +++ /dev/null @@ -1,197 +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 - -version: "2" -run: - modules-download-mode: readonly - timeout: 10m0s # none by default in v2 - -formatters: - enable: - - gofmt - - goimports - settings: - goimports: - # Put local imports after 3rd-party packages - local-prefixes: - - github.com/ironcore-dev/network-operator - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ - -issues: - # '0' disables the following options - max-issues-per-linter: 0 - max-same-issues: 0 - -linters: - # Disable all pre-enabled linters and enable them explicitly so that a newer version does not introduce new linters unexpectedly - default: none - enable: - - bodyclose - - containedctx - - copyloopvar - - dupword - - durationcheck - - errcheck - - errname - - errorlint - - exptostd - - forbidigo - - ginkgolinter - - gocheckcompilerdirectives - - goconst - - gocritic - - gomoddirectives - - gosec - - govet - - ineffassign - - intrange - - misspell - - nilerr - - nolintlint - - nosprintfhostport - - perfsprint - - predeclared - - rowserrcheck - - sqlclosecheck - - staticcheck - - unconvert - - unparam - - unused - - usestdlibvars - - usetesting - - whitespace - settings: - errcheck: - check-type-assertions: false - # Report about assignment of errors to blank identifier. - check-blank: true - # Do not report about not checking of errors in type assertions. - # This is not as dangerous as skipping error values because an unchecked type assertion just immediately panics. - # We disable this because it makes a ton of useless noise esp. in test code. - forbidigo: - analyze-types: true # required for pkg: - forbid: - # ioutil package has been deprecated: https://github.com/golang/go/issues/42026 - - pattern: ^ioutil\..*$ - # Using http.DefaultServeMux is discouraged because it's a global variable that some packages silently and magically add handlers to (esp. net/http/pprof). - # Applications wishing to use http.ServeMux should obtain local instances through http.NewServeMux() instead of using the global default instance. - - pattern: ^http\.DefaultServeMux$ - - pattern: ^http\.Handle(?:Func)?$ - - pkg: ^gopkg\.in/square/go-jose\.v2$ - msg: gopk.in/square/go-jose is archived and has CVEs. Replace it with gopkg.in/go-jose/go-jose.v2 - - pkg: ^github.com/coreos/go-oidc$ - msg: github.com/coreos/go-oidc depends on gopkg.in/square/go-jose which has CVEs. Replace it with github.com/coreos/go-oidc/v3 - - pkg: ^github.com/howeyc/gopass$ - msg: github.com/howeyc/gopass is archived, use golang.org/x/term instead - goconst: - min-occurrences: 5 - gocritic: - enabled-checks: - - boolExprSimplify - - builtinShadow - - emptyStringTest - - evalOrder - - httpNoBody - - importShadow - - initClause - - methodExprCall - - paramTypeCombine - - preferFilepathJoin - - ptrToRefParam - - redundantSprint - - returnAfterHttpError - - stringConcatSimplify - - timeExprSimplify - - truncateCmp - - typeAssertChain - - typeUnparen - - unnamedResult - - unnecessaryBlock - - unnecessaryDefer - - weakCond - - yodaStyleExpr - gomoddirectives: - replace-allow-list: - # for go-pmtud - - github.com/mdlayher/arp - # for github.com/sapcc/vpa_butler - - k8s.io/client-go - # until https://github.com/openconfig/ygnmi/pull/157 is merged - - github.com/openconfig/ygnmi - # for CVE-2025-22868 - - golang.org/x/oauth2 - toolchain-forbidden: true - go-version-pattern: 1\.\d+(\.0)?$ - gosec: - excludes: - # gosec wants us to set a short ReadHeaderTimeout to avoid Slowloris attacks, but doing so would expose us to Keep-Alive race conditions (see https://iximiuz.com/en/posts/reverse-proxy-http-keep-alive-and-502s/ - - G112 - # created file permissions are restricted by umask if necessary - - G306 - govet: - disable: - - fieldalignment - enable-all: true - nolintlint: - require-specific: true - staticcheck: - dot-import-whitelist: - - github.com/majewsky/gg/option - - github.com/onsi/ginkgo/v2 - - github.com/onsi/gomega - usestdlibvars: - http-method: true - http-status-code: true - time-weekday: true - time-month: true - time-layout: true - crypto-hash: true - default-rpc-path: true - sql-isolation-level: true - tls-signature-scheme: true - constant-kind: true - usetesting: - os-temp-dir: true - whitespace: - # Enforce newlines (or comments) after multi-line function signatures. - multi-func: true - exclusions: - generated: lax - presets: - - comments - - common-false-positives - - legacy - - std-error-handling - rules: - - linters: - - bodyclose - path: _test\.go - # It is idiomatic Go to reuse the name 'err' with ':=' for subsequent errors. - # Ref: https://go.dev/doc/effective_go#redeclaration - - path: (.+)\.go$ - text: declaration of "err" shadows declaration at - - linters: - - goconst - path: (.+)_test\.go - paths: - - third_party$ - - builtin$ - - examples$ - - internal/provider/openconfig - - internal/provider/cisco/nxos/genyang - -output: - formats: - text: - # Do not print lines of code with issue. - print-issued-lines: false diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..c76afe02 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,93 @@ +version: "2" +run: + allow-parallel-runners: true + timeout: 10m +linters: + default: none + enable: + - copyloopvar + - dupl + - errcheck + - ginkgolinter + - goconst + - gocyclo + - govet + - ineffassign + - lll + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - unconvert + - unparam + - unused + settings: + revive: + rules: + - name: comment-spacings + - name: import-shadowing + exclusions: + generated: lax + rules: + - linters: + - lll + path: api/* + - linters: + - dupl + - lll + path: internal/controller + - linters: + - lll + path: cmd/* + - linters: + - lll + path: test + - linters: + - lll + path: hack/provider + - linters: + - gocyclo + - dupl + path: internal/provider/cisco/nxos/vrf + - linters: + - lll + path: internal/provider/cisco/nxos + - linters: + - lll + path: internal/provider/openconfig + - linters: + - staticcheck + - goconst + - unconvert + - misspell + - gofmt + - gocyclo + - dupl + path: internal/provider/cisco/nxos/genyang + - linters: + - staticcheck + - goconst + - unused + - unconvert + - gofmt + - errcheck + path: internal/provider/openconfig + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ +issues: + max-issues-per-linter: 10 + max-same-issues: 10 + diff --git a/Dockerfile b/Dockerfile index b666e722..b1645e3b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors # SPDX-License-Identifier: Apache-2.0 -FROM golang:1.25-alpine3.22 AS builder +FROM golang:1.24-alpine3.22 AS builder RUN apk add --no-cache --no-progress git make @@ -14,31 +14,33 @@ ARG TARGETOS ARG TARGETARCH WORKDIR /workspace - -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 install - -FROM gcr.io/distroless/static:nonroot - -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 /usr/bin/network-operator /manager - -USER 65532:65532 +# 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 go mod download + +# Copy the go source +COPY cmd/main.go cmd/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. +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 -ldflags="-s -w" -a -o manager cmd/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 +LABEL source_repository="https://github.com/ironcore-dev/metal-operator" WORKDIR / -ENTRYPOINT [ "/manager" ] +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index 5a58ea6b..23afced5 100644 --- a/Makefile +++ b/Makefile @@ -1,351 +1,324 @@ -################################################################################ -# 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 +# Image URL to use all building/pushing image targets +IMG ?= controller:latest -default: build-all +# 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 $(shell git ls-files '*.go' | grep -v '^internal/provider/openconfig|internal/provider/cisco/nxos/genyang') - -# Run the e2e tests against a k8s cluster. -test-e2e: FORCE - @command -v kind >/dev/null 2>&1 || { \ - echo "Kind is not installed. Please install Kind manually."; \ - exit 1; \ +# 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 gofumpt ## Run goimports against code. + $(GOIMPORTS) -w . + $(GOFUMPT) -l -w $(shell git ls-files '*.go' | grep -E -v 'internal/provider/openconfig|internal/provider/cisco/nxos/genyang') + +.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. +# CertManager is installed by default; skip with: +# - CERT_MANAGER_INSTALL_SKIP=true +KIND_CLUSTER ?= network-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&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; \ - } - @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 - @printf "\e[1;36m>> kustomize build config/crd | kubectl apply -f -\e[0m\n" - @kustomize build config/crd | kubectl apply -f - - -# Uninstall CRDs from the k8s cluster -undeploy-crds: FORCE install-kustomize - @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 - - -# 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 - -netop-provider: - @printf "\e[1;36m>> go build -o build/netop-provider ./hack/provider\e[0m\n" - @go build -o build/netop-provider ./hack/provider - @printf "\e[1;36m>> ./build/netop-provider --help\e[0m\n" - @./build/netop-provider --help - -TEST_SERVER_IMG ?= ghcr.io/ironcore-dev/gnmi-test-server:latest - -build-test-gnmi-server: FORCE - @printf "\e[1;36m>> $(CONTAINER_TOOL) build --tag=$(TEST_SERVER_IMG) ./test/gnmi\e[0m\n" - @$(CONTAINER_TOOL) build --tag=$(TEST_SERVER_IMG) ./test/gnmi - -run-test-gnmi-server: FORCE build-test-gnmi-server - @printf "\e[1;36m>> $(CONTAINER_TOOL) run -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG)\e[0m\n" - @$(CONTAINER_TOOL) run --rm -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG) - -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"; if command -v curl >/dev/null 2>&1; then GET="curl -sLo-"; elif command -v wget >/dev/null 2>&1; then GET="wget -O-"; else echo "Didn't find curl or wget to download shellcheck"; exit 2; fi; $$GET "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-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 - -install-reuse: FORCE - @if ! hash reuse 2>/dev/null; then if ! hash pip3 2>/dev/null; then printf "\e[1;31m>> Cannot install reuse because no pip3 was found. Either install it using your package manager or install pip3\e[0m\n"; else printf "\e[1;36m>> Installing reuse...\e[0m\n"; pip3 install --user reuse; fi; fi - -prepare-static-check: FORCE install-golangci-lint install-modernize install-shellcheck install-go-licence-detector install-addlicense install-reuse - -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 - -DESTDIR = -ifeq ($(shell uname -s),Darwin) - PREFIX = /usr/local -else - PREFIX = /usr + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) + + +.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 contributors' {} + + +.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/main.go + +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/manager/main.go + +.PHONY: helm +helm: manifests kubebuilder + $(KUBEBUILDER) edit --plugins=helm/v1-alpha + +# 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: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + $(CONTAINER_TOOL) push ${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 foo-builder + $(CONTAINER_TOOL) buildx use foo-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm foo-builder + rm Dockerfile.crosss + +.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=${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 ## 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=${IMG} + $(KUSTOMIZE) build config/default | $(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 - + +##@ Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +CURL_RETRIES=3 + +## Tool Binaries +KUBECTL ?= kubectl +KIND ?= kind +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 +GOFUMPT ?= $(LOCALBIN)/gofumpt +NETOP_PROVIDER ?= $(LOCALBIN)/netop-provider + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.6.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.4 +GOIMPORTS_VERSION ?= v0.31.0 +GEN_CRD_API_REFERENCE_DOCS_VERSION ?= v0.3.0 +KUBEBUILDER_VERSION ?= v4.5.1 +ADDLICENSE_VERSION ?= v1.1.1 +GOFUMPT_VERSION ?= v0.8.0 + +.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: 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' | grep -Ev '/internal/provider/openconfig|internal/provider/cisco/nxos/genyang') -# 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 generate install-setup-envtest | build - @printf "\e[1;36m>> Running tests\e[0m\n" - KUBEBUILDER_ASSETS=$$(setup-envtest use 1.32 -p path) go run github.com/onsi/ginkgo/v2/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 install-reuse - @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 install-reuse - @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-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[36minstall-reuse\e[0m Install reuse required by license-headers/check-reuse\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)) + +.PHONY: netop-provider +netop-provider: $(NETOP_PROVIDER) ## Install the network operator provider binary. +$(NETOP_PROVIDER): $(LOCALBIN) + go build -o $(NETOP_PROVIDER) ./hack/provider/main.go + +.PHONY: gofumpt +gofumpt: $(GOFUMPT) ## Download gofumpt locally if necessary. +$(GOFUMPT): $(LOCALBIN) + $(call go-install-tool,$(GOFUMPT),mvdan.cc/gofumpt,$(GOFUMPT_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/Makefile.maker.yaml b/Makefile.maker.yaml deleted file mode 100644 index af071d1c..00000000 --- a/Makefile.maker.yaml +++ /dev/null @@ -1,182 +0,0 @@ -# Configuration file for - -metadata: - url: https://github.com/ironcore-dev/network-operator - -binaries: - - name: network-operator - fromPackage: ./cmd - installTo: bin/ - -controllerGen: - enabled: true - crdOutputPath: config/crd/bases - objectHeaderFile: hack/boilerplate.go.txt - rbacRoleName: manager-role - -coverageTest: - only: "/internal" - except: "/internal/provider/openconfig|internal/provider/cisco/nxos/genyang" - -dockerfile: - enabled: false - -golang: - setGoModVersion: true - -golangciLint: - createConfig: true - skipDirs: - - internal/provider/openconfig - - internal/provider/cisco/nxos/genyang - timeout: 10m - -goReleaser: - createConfig: true - -reuse: - enabled: false - -renovate: - enabled: true - assignees: - - felix-kaestner - -testPackages: - except: '/test/e2e' - -githubWorkflow: - global: - defaultBranch: main - ci: - enabled: true - coveralls: false - prepareMakeTarget: generate - license: - enabled: true - release: - enabled: true - securityChecks: - enabled: true - -variables: - GO_BUILDENV: 'CGO_ENABLED=0' - -verbatim: | - # Image to use all building/pushing image targets - IMG ?= controller:latest - - # 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). - 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 $(shell git ls-files '*.go' | grep -v '^internal/provider/openconfig|internal/provider/cisco/nxos/genyang') - - # Run the e2e tests against a k8s cluster. - test-e2e: 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' || { \ - 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 - @printf "\e[1;36m>> kustomize build config/crd | kubectl apply -f -\e[0m\n" - @kustomize build config/crd | kubectl apply -f - - - # Uninstall CRDs from the k8s cluster - undeploy-crds: FORCE install-kustomize - @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 - - - # 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 - - netop-provider: - @printf "\e[1;36m>> go build -o build/netop-provider ./hack/provider\e[0m\n" - @go build -o build/netop-provider ./hack/provider - @printf "\e[1;36m>> ./build/netop-provider --help\e[0m\n" - @./build/netop-provider --help - - TEST_SERVER_IMG ?= ghcr.io/ironcore-dev/gnmi-test-server:latest - - build-test-gnmi-server: FORCE - @printf "\e[1;36m>> $(CONTAINER_TOOL) build --tag=$(TEST_SERVER_IMG) ./test/gnmi\e[0m\n" - @$(CONTAINER_TOOL) build --tag=$(TEST_SERVER_IMG) ./test/gnmi - - run-test-gnmi-server: FORCE build-test-gnmi-server - @printf "\e[1;36m>> $(CONTAINER_TOOL) run -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG)\e[0m\n" - @$(CONTAINER_TOOL) run --rm -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG) diff --git a/api/v1alpha1/device_types.go b/api/v1alpha1/device_types.go index 0e84f7b3..7e7bda13 100644 --- a/api/v1alpha1/device_types.go +++ b/api/v1alpha1/device_types.go @@ -491,11 +491,11 @@ type DeviceStatus struct { Phase DevicePhase `json:"phase,omitempty"` // The conditions are a list of status objects that describe the state of the Device. - //+listType=map - //+listMapKey=type - //+patchStrategy=merge - //+patchMergeKey=type - //+optional + // +listType=map + // +listMapKey=type + // +patchStrategy=merge + // +patchMergeKey=type + // +optional Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` } diff --git a/api/v1alpha1/doc.go b/api/v1alpha1/doc.go new file mode 100644 index 00000000..f093f72f --- /dev/null +++ b/api/v1alpha1/doc.go @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +// Package v1alpha1 contains API Schema definitions for the networking.cloud.sap API group +// +groupName=networking.cloud.sap +// +kubebuilder:object:generate=true +package v1alpha1 diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ae2ddc84..adf2c2a6 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -8,7 +8,7 @@ package v1alpha1 import ( - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/cmd/main.go b/cmd/main.go index fd87ae91..2e78564c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -11,23 +11,16 @@ import ( "path/filepath" "strings" - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. - _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/utils/ptr" - - // Set runtime concurrency to match CPU limit imposed by Kubernetes - _ "go.uber.org/automaxprocs" - - "github.com/sapcc/go-api-declarations/bininfo" "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/certwatcher" - "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -56,9 +49,6 @@ func init() { } func main() { - // if called with `--version`, report version and exit - bininfo.HandleVersionArgument() - var metricsAddr string var metricsCertPath, metricsCertName, metricsCertKey string var webhookCertPath, webhookCertName, webhookCertKey string @@ -82,6 +72,7 @@ func main() { flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") flag.StringVar(&watchFilterValue, "watch-filter", "", fmt.Sprintf("Label value that the controller watches to reconcile api objects. Label key is always %q. If unspecified, the controller watches for all api objects.", v1alpha1.WatchLabel)) flag.StringVar(&providerName, "provider", "openconfig", "The provider to use for the controller. If not specified, the default provider is used. Available providers: "+strings.Join(provider.Providers(), ", ")) + opts := zap.Options{ Development: true, TimeEncoder: zapcore.ISO8601TimeEncoder, @@ -179,8 +170,6 @@ func main() { } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Cache: cache.Options{ReaderFailOnMissingInformer: true}, - Controller: config.Controller{UsePriorityQueue: ptr.To(true)}, Scheme: scheme, Metrics: metricsServerOptions, WebhookServer: webhookServer, diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index ce228eac..37d2b63f 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -58,41 +58,42 @@ spec: seccompProfile: type: RuntimeDefault containers: - - command: - - /manager - args: - - --leader-elect - - --health-probe-bind-address=:8081 - image: controller:latest - imagePullPolicy: IfNotPresent - name: manager - ports: [] - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - # TODO(user): Configure the resources accordingly based on the project requirements. - # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - requests: - cpu: 50m - memory: 256Mi - limits: - memory: 512Mi - volumeMounts: [] + - command: + - /manager + args: + - --leader-elect + - --health-probe-bind-address=:8081 + image: controller:latest + name: manager + ports: [] + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 1000m + memory: 512Mi + requests: + cpu: 10m + memory: 64Mi + volumeMounts: [] volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/config/network-policy/allow-metrics-traffic.yaml b/config/network-policy/allow-metrics-traffic.yaml index 7defccca..39e122bf 100644 --- a/config/network-policy/allow-metrics-traffic.yaml +++ b/config/network-policy/allow-metrics-traffic.yaml @@ -19,9 +19,9 @@ spec: ingress: # This allows ingress traffic from any namespace with the label metrics: enabled - from: - - namespaceSelector: - matchLabels: - metrics: enabled # Only from namespaces with this label + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label ports: - port: 8443 protocol: TCP 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/crd/networking.cloud.sap_devices.yaml b/dist/chart/templates/crd/networking.cloud.sap_devices.yaml new file mode 100755 index 00000000..d74f4dd9 --- /dev/null +++ b/dist/chart/templates/crd/networking.cloud.sap_devices.yaml @@ -0,0 +1,777 @@ +{{- if .Values.crd.enable }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + annotations: + {{- if .Values.crd.keep }} + "helm.sh/resource-policy": keep + {{- end }} + controller-gen.kubebuilder.io/version: v0.18.0 + name: devices.networking.cloud.sap +spec: + group: networking.cloud.sap + names: + kind: Device + listKind: DeviceList + plural: devices + singular: device + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.endpoint.address + name: Endpoint + type: string + - jsonPath: .status.phase + name: Phase + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Device is the Schema for the devices API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired state of the resource. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + acl: + description: Access Control Lists (ACLs) configuration. + items: + properties: + entries: + description: A list of rules/entries to apply. + items: + properties: + action: + description: The forwarding action of the ACL entry. + enum: + - Permit + - Deny + type: string + destinationAddress: + description: Destination IPv4 address prefix. Use 0.0.0.0/0 + to represent 'any'. + pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$ + type: string + protocol: + default: ip + description: The protocol to match. If not specified, + defaults to "ip" (IPv4). + enum: + - ahp + - eigrp + - esp + - gre + - icmp + - igmp + - ip + - nos + - ospf + - pcp + - pim + - tcp + - udf + - udp + type: string + sequence: + description: The sequence number of the ACL entry. + type: integer + sourceAddress: + description: Source IPv4 address prefix. Use 0.0.0.0/0 + to represent 'any'. + pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$ + type: string + required: + - action + - destinationAddress + - sequence + - sourceAddress + type: object + minItems: 1 + type: array + name: + description: The name of the access control list. + type: string + required: + - entries + - name + type: object + type: array + banner: + description: MOTD banner to display on login. + properties: + configMapRef: + description: Reference to a ConfigMap containing the template + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + inline: + description: Inline template content + type: string + secretRef: + description: Reference to a Secret containing the template + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be + defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: exactly one of 'inline', 'secretRef', or 'configMapRef' + must be specified + rule: '[has(self.inline), has(self.secretRef), has(self.configMapRef)].filter(x, + x).size() == 1' + bootstrap: + description: |- + Bootstrap is an optional configuration for the device bootstrap process. + It can be used to provide initial configuration templates or scripts that are applied during the device provisioning. + properties: + template: + description: Template defines the multiline string template that + contains the initial configuration for the device. + properties: + configMapRef: + description: Reference to a ConfigMap containing the template + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + inline: + description: Inline template content + type: string + secretRef: + description: Reference to a Secret containing the template + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: exactly one of 'inline', 'secretRef', or 'configMapRef' + must be specified + rule: '[has(self.inline), has(self.secretRef), has(self.configMapRef)].filter(x, + x).size() == 1' + required: + - template + type: object + dns: + description: Top-level configuration for DNS / resolver. + properties: + domain: + description: Default domain name that the switch uses to complete + unqualified hostnames. + format: hostname + type: string + servers: + description: A list of DNS servers to use for address resolution. + items: + properties: + address: + description: The Hostname or IP address of the DNS server. + type: string + networkInstance: + description: The network instance used to communicate with + the DNS server. + type: string + required: + - address + type: object + maxItems: 6 + type: array + srcIf: + description: Source interface for all DNS traffic. + type: string + required: + - domain + type: object + endpoint: + description: Endpoint contains the connection information for the + device. + properties: + address: + description: Address is the management address of the device provided + as . + pattern: ^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$ + type: string + secretRef: + description: |- + SecretRef is name of the authentication secret for the device containing the username and password. + The secret must be of type kubernetes.io/basic-auth and as such contain the following keys: 'username' and 'password'. + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + tls: + description: Transport credentials for grpc connection to the + switch. + properties: + ca: + description: The CA certificate to verify the server's identity. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + certificate: + description: |- + The client certificate and private key to use for mutual TLS authentication. + Leave empty if mTLS is not desired. + properties: + secretRef: + description: |- + Secret containing the certificate. + The secret must be of type kubernetes.io/tls and as such contain the following keys: 'tls.crt' and 'tls.key'. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + required: + - secretRef + type: object + required: + - ca + type: object + required: + - address + type: object + grpc: + description: |- + Configuration for the gRPC server on the device. + Currently, only a single "default" gRPC server is supported. + properties: + certificateId: + description: |- + Name of the certificate that is associated with the gRPC service. + The certificate is provisioned through other interfaces on the device, + such as e.g. the gNOI certificate management service. + type: string + gnmi: + description: |- + Additional gNMI configuration for the gRPC server. + This may not be supported by all devices. + properties: + keepAliveTimeout: + default: 10m + description: |- + Configure the keepalive timeout for inactive or unauthorized connections. + The gRPC agent is expected to periodically send an empty response to the client, on which the client is expected to respond with an empty request. + If the client does not respond within the keepalive timeout, the gRPC agent should close the connection. + The default interval value is 10 minutes. + pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ + type: string + maxConcurrentCall: + default: 8 + description: |- + The maximum number of concurrent gNMI calls that can be made to the gRPC server on the switch for each VRF. + Configure a limit from 1 through 16. The default limit is 8. + maximum: 16 + minimum: 1 + type: integer + minSampleInterval: + default: 10s + description: |- + Configure the minimum sample interval for the gNMI telemetry stream. + Once per stream sample interval, the switch sends the current values for all specified paths. + The default value is 10 seconds. + pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ + type: string + type: object + networkInstance: + description: Enable the gRPC agent to accept incoming (dial-in) + RPC requests from a given network instance. + type: string + port: + default: 9339 + description: |- + The TCP port on which the gRPC server should listen. + The range of port-id is from 1024 to 65535. + Port 9339 is the default. + format: int32 + maximum: 65535 + minimum: 1024 + type: integer + type: object + logging: + description: Top-level logging configuration for the device. + properties: + facilities: + description: Facilities is a list of log facilities to configure + on the device. + items: + properties: + name: + description: The name of the log facility. + type: string + severity: + description: The severity level of the log messages for + this facility. + enum: + - Debug + - Info + - Notice + - Warning + - Error + - Critical + - Alert + - Emergency + type: string + required: + - name + - severity + type: object + minItems: 1 + type: array + servers: + description: Servers is a list of remote log servers to which + the device will send logs. + items: + properties: + address: + description: IP address or hostname of the remote log server + type: string + networkInstance: + description: The network instance used to reach the log + server. + type: string + port: + default: 514 + description: |- + The destination port number for syslog UDP messages to + the server. The default is 514. + format: int64 + type: integer + severity: + description: The servity level of the log messages sent + to the server. + enum: + - Debug + - Info + - Notice + - Warning + - Error + - Critical + - Alert + - Emergency + type: string + required: + - address + - networkInstance + - severity + type: object + minItems: 1 + type: array + required: + - facilities + - servers + type: object + ntp: + description: Configuration data for system-wide NTP process. + properties: + servers: + description: NTP servers. + items: + properties: + address: + description: Hostname/IP address of the NTP server. + type: string + networkInstance: + description: The network instance used to communicate with + the NTP server. + type: string + prefer: + default: false + description: Indicates whether this server should be preferred + or not. + type: boolean + required: + - address + type: object + minItems: 1 + type: array + srcIf: + description: Source interface for all NTP traffic. + type: string + required: + - servers + - srcIf + type: object + pki: + description: PKI configuration for managing certificates on the device. + properties: + certificates: + description: Certificates is a list of certificates to be managed + by the PKI. + items: + properties: + name: + description: The name of the certificate. + type: string + source: + description: The source of the certificate content. + properties: + secretRef: + description: |- + Secret containing the certificate. + The secret must be of type kubernetes.io/tls and as such contain the following keys: 'tls.crt' and 'tls.key'. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within + which the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + required: + - secretRef + type: object + required: + - name + - source + type: object + minItems: 1 + type: array + required: + - certificates + type: object + snmp: + description: SNMP global configuration. + properties: + communities: + description: SNMP communities for SNMPv1 or SNMPv2c. + items: + properties: + acl: + description: ACL name to filter snmp requests. + type: string + group: + description: Group to which the community belongs. + type: string + name: + description: Name of the community. + type: string + type: object + type: array + contact: + description: The contact information for the SNMP server. + type: string + destinations: + description: SNMP destinations for SNMP traps or informs. + items: + properties: + address: + description: The Hostname or IP address of the SNMP host + to send notifications to. + type: string + networkInstance: + description: The network instance to use to source traffic. + type: string + target: + description: SNMP community or user name. + type: string + type: + default: Traps + description: Type of message to send to host. Default is + traps. + enum: + - Traps + - Inform + type: string + version: + default: v2c + description: SNMP version. Default is v2c. + enum: + - v1 + - v2c + - v3 + type: string + required: + - address + type: object + minItems: 1 + type: array + engineId: + description: The SNMP engine ID for the SNMP server. + type: string + location: + description: The location information for the SNMP server. + type: string + srcIf: + description: Source interface to be used for sending out SNMP + Trap/Inform notifications. + type: string + traps: + description: The list of trap groups to enable. + items: + type: string + type: array + required: + - contact + - destinations + - engineId + - location + - srcIf + type: object + users: + description: List of local users on the switch. + items: + properties: + name: + description: Assigned username for this user. + type: string + password: + description: The user password, supplied as cleartext. + properties: + secretKeyRef: + description: Selects a key of a secret. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + required: + - secretKeyRef + type: object + role: + description: Role which the user is to be assigned to. + type: string + required: + - name + - password + - role + type: object + type: array + required: + - endpoint + type: object + status: + description: |- + Status of the resource. This is set and updated automatically. + Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + conditions: + description: The conditions are a list of status objects that describe + the state of the Device. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + phase: + default: Pending + description: Phase represents the current phase of the Device. + enum: + - Pending + - Provisioning + - Active + - Failed + type: string + required: + - phase + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end -}} diff --git a/dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml b/dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml new file mode 100755 index 00000000..2787712a --- /dev/null +++ b/dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml @@ -0,0 +1,241 @@ +{{- if .Values.crd.enable }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + annotations: + {{- if .Values.crd.keep }} + "helm.sh/resource-policy": keep + {{- end }} + controller-gen.kubebuilder.io/version: v0.18.0 + name: interfaces.networking.cloud.sap +spec: + group: networking.cloud.sap + names: + kind: Interface + listKind: InterfaceList + plural: interfaces + shortNames: + - int + singular: interface + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.name + name: Interface + type: string + - jsonPath: .spec.adminState + name: Admin State + type: string + - jsonPath: .spec.description + name: Description + type: string + - jsonPath: .spec.mtu + name: MTU + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Interface is the Schema for the interfaces API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the desired state of the resource. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + adminState: + description: AdminState indicates whether the interface is administratively + up or down. + enum: + - Up + - Down + type: string + description: + description: Description provides a human-readable description of + the interface. + maxLength: 255 + type: string + ipv4Addresses: + description: |- + Ipv4Addresses is the list of IPv4 addresses assigned to the interface. + Each address should be given either in CIDR notation (e.g., "10.0.0.1/32") + or as interface reference in the form of "unnumbered:" (e.g., "unnumbered:lo0"). + items: + pattern: ^(?:(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}?|unnumbered:[\w-]+)$ + type: string + type: array + mtu: + description: MTU (Maximum Transmission Unit) specifies the size of + the largest packet that can be sent over the interface. + format: int32 + maximum: 9216 + minimum: 576 + type: integer + name: + description: Name is the name of the interface. + maxLength: 255 + type: string + switchport: + description: |- + Switchport defines the switchport configuration for the interface. + This is only applicable for interfaces that are switchports (e.g., Ethernet interfaces). + properties: + accessVlan: + description: |- + AccessVlan specifies the VLAN ID for access mode switchports. + Only applicable when Mode is set to "Access". + format: int32 + maximum: 4094 + minimum: 1 + type: integer + allowedVlans: + description: |- + AllowedVlans is a list of VLAN IDs that are allowed on the trunk port. + Only applicable when Mode is set to "Trunk". + items: + format: int32 + maximum: 4094 + minimum: 1 + type: integer + minItems: 1 + type: array + mode: + description: Mode defines the switchport mode, such as access + or trunk. + enum: + - Access + - Trunk + type: string + nativeVlan: + description: |- + NativeVlan specifies the native VLAN ID for trunk mode switchports. + Only applicable when Mode is set to "Trunk". + format: int32 + maximum: 4094 + minimum: 1 + type: integer + required: + - mode + type: object + x-kubernetes-validations: + - message: accessVlan must be specified when mode is Access + rule: self.mode != 'Access' || has(self.accessVlan) + - message: nativeVlan must be specified when mode is Trunk + rule: self.mode != 'Trunk' || has(self.nativeVlan) + - message: allowedVlans must be specified when mode is Trunk + rule: self.mode != 'Trunk' || has(self.allowedVlans) + type: + description: Type indicates the type of the interface. + enum: + - Physical + - Loopback + type: string + required: + - adminState + - name + - type + type: object + x-kubernetes-validations: + - message: switchport and ipv4Addresses are mutually exclusive + rule: '!has(self.switchport) || !has(self.ipv4Addresses)' + - message: switchport must not be specified for interfaces of type Loopback + rule: self.type != 'Loopback' || !has(self.switchport) + status: + description: |- + Status of the resource. This is set and updated automatically. + Read-only. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + conditions: + description: The conditions are a list of status objects that describe + the state of the Interface. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- 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..4d381a54 --- /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/device_admin_role.yaml b/dist/chart/templates/rbac/device_admin_role.yaml new file mode 100755 index 00000000..bc1ea098 --- /dev/null +++ b/dist/chart/templates/rbac/device_admin_role.yaml @@ -0,0 +1,28 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project network-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over networking.cloud.sap. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: device-admin-role +rules: +- apiGroups: + - networking.cloud.sap + resources: + - devices + verbs: + - '*' +- apiGroups: + - networking.cloud.sap + resources: + - devices/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/device_editor_role.yaml b/dist/chart/templates/rbac/device_editor_role.yaml new file mode 100755 index 00000000..5eae2b03 --- /dev/null +++ b/dist/chart/templates/rbac/device_editor_role.yaml @@ -0,0 +1,34 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project network-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the networking.cloud.sap. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: device-editor-role +rules: +- apiGroups: + - networking.cloud.sap + resources: + - devices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.cloud.sap + resources: + - devices/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/device_viewer_role.yaml b/dist/chart/templates/rbac/device_viewer_role.yaml new file mode 100755 index 00000000..74f2eb9a --- /dev/null +++ b/dist/chart/templates/rbac/device_viewer_role.yaml @@ -0,0 +1,30 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project network-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to networking.cloud.sap resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: device-viewer-role +rules: +- apiGroups: + - networking.cloud.sap + resources: + - devices + verbs: + - get + - list + - watch +- apiGroups: + - networking.cloud.sap + resources: + - devices/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/interface_admin_role.yaml b/dist/chart/templates/rbac/interface_admin_role.yaml new file mode 100755 index 00000000..a98f8d8b --- /dev/null +++ b/dist/chart/templates/rbac/interface_admin_role.yaml @@ -0,0 +1,28 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project network-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over networking.cloud.sap. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: interface-admin-role +rules: +- apiGroups: + - networking.cloud.sap + resources: + - interfaces + verbs: + - '*' +- apiGroups: + - networking.cloud.sap + resources: + - interfaces/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/interface_editor_role.yaml b/dist/chart/templates/rbac/interface_editor_role.yaml new file mode 100755 index 00000000..f0ac3564 --- /dev/null +++ b/dist/chart/templates/rbac/interface_editor_role.yaml @@ -0,0 +1,34 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project network-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the networking.cloud.sap. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: interface-editor-role +rules: +- apiGroups: + - networking.cloud.sap + resources: + - interfaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.cloud.sap + resources: + - interfaces/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/interface_viewer_role.yaml b/dist/chart/templates/rbac/interface_viewer_role.yaml new file mode 100755 index 00000000..0f2b1c41 --- /dev/null +++ b/dist/chart/templates/rbac/interface_viewer_role.yaml @@ -0,0 +1,30 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project network-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to networking.cloud.sap resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: interface-viewer-role +rules: +- apiGroups: + - networking.cloud.sap + resources: + - interfaces + verbs: + - get + - list + - watch +- apiGroups: + - networking.cloud.sap + resources: + - interfaces/status + verbs: + - get +{{- 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..d3ef419a --- /dev/null +++ b/dist/chart/templates/rbac/role.yaml @@ -0,0 +1,63 @@ +{{- 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: + - configmaps + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - update + - watch +- apiGroups: + - networking.cloud.sap + resources: + - devices + - interfaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.cloud.sap + resources: + - devices/finalizers + - interfaces/finalizers + verbs: + - update +- apiGroups: + - networking.cloud.sap + resources: + - devices/status + - interfaces/status + verbs: + - get + - patch + - update +{{- 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/docs/api-reference/api.md b/docs/api-reference/api.md new file mode 100644 index 00000000..364b5d2d --- /dev/null +++ b/docs/api-reference/api.md @@ -0,0 +1,2166 @@ +

Packages:

+ +

networking.cloud.sap/v1alpha1

+
+

Package v1alpha1 contains API Schema definitions for the networking.cloud.sap API group

+
+Resource Types: +
    +

    ACL +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    The name of the access control list.

    +
    +entries
    + + +[]*./api/v1alpha1.ACLEntry + + +
    +

    A list of rules/entries to apply.

    +
    +

    ACLAction +(string alias)

    +

    +(Appears on:ACLEntry) +

    +
    +

    ACLAction represents the type of action that can be taken by an ACL rule.

    +
    + + + + + + + + + + + + +
    ValueDescription

    "Deny"

    ActionDeny blocks traffic that matches the rule.

    +

    "Permit"

    ActionPermit allows traffic that matches the rule.

    +
    +

    ACLEntry +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +sequence
    + +int + +
    +

    The sequence number of the ACL entry.

    +
    +action
    + + +ACLAction + + +
    +

    The forwarding action of the ACL entry.

    +
    +protocol
    + +string + +
    +(Optional) +

    The protocol to match. If not specified, defaults to “ip” (IPv4).

    +
    +sourceAddress
    + + +IPPrefix + + +
    +

    Source IPv4 address prefix. Use 0.0.0.0/0 to represent ‘any’.

    +
    +destinationAddress
    + + +IPPrefix + + +
    +

    Destination IPv4 address prefix. Use 0.0.0.0/0 to represent ‘any’.

    +
    +

    AdminState +(string alias)

    +

    +(Appears on:InterfaceSpec) +

    +
    +

    AdminState represents the administrative state of the interface.

    +
    + + + + + + + + + + + + +
    ValueDescription

    "Down"

    AdminStateDown indicates that the interface is administratively set down.

    +

    "Up"

    AdminStateUp indicates that the interface is administratively set up.

    +
    +

    Bootstrap +

    +

    +(Appears on:DeviceSpec) +

    +
    +

    Bootstrap defines the configuration for device bootstrap.

    +
    + + + + + + + + + + + + + +
    FieldDescription
    +template
    + + +TemplateSource + + +
    +

    Template defines the multiline string template that contains the initial configuration for the device.

    +
    +

    Certificate +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    The name of the certificate.

    +
    +source
    + + +CertificateSource + + +
    +

    The source of the certificate content.

    +
    +

    CertificateSource +

    +

    +(Appears on:Certificate, TLS) +

    +
    +

    CertificateSource represents a source for the value of a certificate.

    +
    + + + + + + + + + + + + + +
    FieldDescription
    +secretRef
    + + +Kubernetes core/v1.SecretReference + + +
    +

    Secret containing the certificate. +The secret must be of type kubernetes.io/tls and as such contain the following keys: ‘tls.crt’ and ‘tls.key’.

    +
    +

    DNS +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +domain
    + +string + +
    +

    Default domain name that the switch uses to complete unqualified hostnames.

    +
    +servers
    + + +[]*./api/v1alpha1.NameServer + + +
    +(Optional) +

    A list of DNS servers to use for address resolution.

    +
    +srcIf
    + +string + +
    +(Optional) +

    Source interface for all DNS traffic.

    +
    +

    Device +

    +
    +

    Device is the Schema for the devices API.

    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +metadata
    + + +Kubernetes meta/v1.ObjectMeta + + +
    +Refer to the Kubernetes API documentation for the fields of the +metadata field. +
    +spec
    + + +DeviceSpec + + +
    +

    Specification of the desired state of the resource. +More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +endpoint
    + + +Endpoint + + +
    +

    Endpoint contains the connection information for the device.

    +
    +bootstrap
    + + +Bootstrap + + +
    +(Optional) +

    Bootstrap is an optional configuration for the device bootstrap process. +It can be used to provide initial configuration templates or scripts that are applied during the device provisioning.

    +
    +dns
    + + +DNS + + +
    +(Optional) +

    Top-level configuration for DNS / resolver.

    +
    +ntp
    + + +NTP + + +
    +(Optional) +

    Configuration data for system-wide NTP process.

    +
    +acl
    + + +[]*./api/v1alpha1.ACL + + +
    +(Optional) +

    Access Control Lists (ACLs) configuration.

    +
    +pki
    + + +PKI + + +
    +(Optional) +

    PKI configuration for managing certificates on the device.

    +
    +logging
    + + +Logging + + +
    +(Optional) +

    Top-level logging configuration for the device.

    +
    +snmp
    + + +SNMP + + +
    +(Optional) +

    SNMP global configuration.

    +
    +users
    + + +[]*./api/v1alpha1.User + + +
    +(Optional) +

    List of local users on the switch.

    +
    +grpc
    + + +GRPC + + +
    +(Optional) +

    Configuration for the gRPC server on the device. +Currently, only a single “default” gRPC server is supported.

    +
    +banner
    + + +TemplateSource + + +
    +(Optional) +

    MOTD banner to display on login.

    +
    +
    +status
    + + +DeviceStatus + + +
    +

    Status of the resource. This is set and updated automatically. +Read-only. +More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    +
    +

    DevicePhase +(string alias)

    +

    +(Appears on:DeviceStatus) +

    +
    +

    DevicePhase represents the current phase of the Device as it’s being provisioned and managed by the operator.

    +
    + + + + + + + + + + + + + + + + +
    ValueDescription

    "Active"

    DevicePhaseActive indicates that the device has been successfully provisioned and is now ready for use.

    +

    "Failed"

    DevicePhaseFailed indicates that the device provisioning has failed.

    +

    "Pending"

    DevicePhasePending indicates that the device is pending and has not yet been provisioned.

    +

    "Provisioning"

    DevicePhaseProvisioning indicates that the device is being provisioned.

    +
    +

    DeviceSpec +

    +

    +(Appears on:Device) +

    +
    +

    DeviceSpec defines the desired state of Device.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +endpoint
    + + +Endpoint + + +
    +

    Endpoint contains the connection information for the device.

    +
    +bootstrap
    + + +Bootstrap + + +
    +(Optional) +

    Bootstrap is an optional configuration for the device bootstrap process. +It can be used to provide initial configuration templates or scripts that are applied during the device provisioning.

    +
    +dns
    + + +DNS + + +
    +(Optional) +

    Top-level configuration for DNS / resolver.

    +
    +ntp
    + + +NTP + + +
    +(Optional) +

    Configuration data for system-wide NTP process.

    +
    +acl
    + + +[]*./api/v1alpha1.ACL + + +
    +(Optional) +

    Access Control Lists (ACLs) configuration.

    +
    +pki
    + + +PKI + + +
    +(Optional) +

    PKI configuration for managing certificates on the device.

    +
    +logging
    + + +Logging + + +
    +(Optional) +

    Top-level logging configuration for the device.

    +
    +snmp
    + + +SNMP + + +
    +(Optional) +

    SNMP global configuration.

    +
    +users
    + + +[]*./api/v1alpha1.User + + +
    +(Optional) +

    List of local users on the switch.

    +
    +grpc
    + + +GRPC + + +
    +(Optional) +

    Configuration for the gRPC server on the device. +Currently, only a single “default” gRPC server is supported.

    +
    +banner
    + + +TemplateSource + + +
    +(Optional) +

    MOTD banner to display on login.

    +
    +

    DeviceStatus +

    +

    +(Appears on:Device) +

    +
    +

    DeviceStatus defines the observed state of Device.

    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +phase
    + + +DevicePhase + + +
    +

    Phase represents the current phase of the Device.

    +
    +conditions
    + + +[]Kubernetes meta/v1.Condition + + +
    +(Optional) +

    The conditions are a list of status objects that describe the state of the Device.

    +
    +

    Endpoint +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +address
    + +string + +
    +

    Address is the management address of the device provided as ip:port.

    +
    +secretRef
    + + +Kubernetes core/v1.SecretReference + + +
    +(Optional) +

    SecretRef is name of the authentication secret for the device containing the username and password. +The secret must be of type kubernetes.io/basic-auth and as such contain the following keys: ‘username’ and ‘password’.

    +
    +tls
    + + +TLS + + +
    +(Optional) +

    Transport credentials for grpc connection to the switch.

    +
    +

    GNMI +

    +

    +(Appears on:GRPC) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +maxConcurrentCall
    + +byte + +
    +(Optional) +

    The maximum number of concurrent gNMI calls that can be made to the gRPC server on the switch for each VRF. +Configure a limit from 1 through 16. The default limit is 8.

    +
    +keepAliveTimeout
    + + +Kubernetes meta/v1.Duration + + +
    +(Optional) +

    Configure the keepalive timeout for inactive or unauthorized connections. +The gRPC agent is expected to periodically send an empty response to the client, on which the client is expected to respond with an empty request. +If the client does not respond within the keepalive timeout, the gRPC agent should close the connection. +The default interval value is 10 minutes.

    +
    +minSampleInterval
    + + +Kubernetes meta/v1.Duration + + +
    +(Optional) +

    Configure the minimum sample interval for the gNMI telemetry stream. +Once per stream sample interval, the switch sends the current values for all specified paths. +The default value is 10 seconds.

    +
    +

    GRPC +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +port
    + +int32 + +
    +(Optional) +

    The TCP port on which the gRPC server should listen. +The range of port-id is from 1024 to 65535. +Port 9339 is the default.

    +
    +certificateId
    + +string + +
    +(Optional) +

    Name of the certificate that is associated with the gRPC service. +The certificate is provisioned through other interfaces on the device, +such as e.g. the gNOI certificate management service.

    +
    +networkInstance
    + +string + +
    +(Optional) +

    Enable the gRPC agent to accept incoming (dial-in) RPC requests from a given network instance.

    +
    +gnmi
    + + +GNMI + + +
    +(Optional) +

    Additional gNMI configuration for the gRPC server. +This may not be supported by all devices.

    +
    +

    IPPrefix +

    +

    +(Appears on:ACLEntry) +

    +
    +

    IPPrefix represents an IP prefix in CIDR notation. +It is used to define a range of IP addresses in a network.

    +
    + + + + + + + + + + + + + +
    FieldDescription
    +-
    + + +net/netip.Prefix + + +
    +
    +

    Interface +

    +
    +

    Interface is the Schema for the interfaces API.

    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +metadata
    + + +Kubernetes meta/v1.ObjectMeta + + +
    +Refer to the Kubernetes API documentation for the fields of the +metadata field. +
    +spec
    + + +InterfaceSpec + + +
    +

    Specification of the desired state of the resource. +More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +name
    + +string + +
    +

    Name is the name of the interface.

    +
    +adminState
    + + +AdminState + + +
    +

    AdminState indicates whether the interface is administratively up or down.

    +
    +description
    + +string + +
    +(Optional) +

    Description provides a human-readable description of the interface.

    +
    +type
    + + +InterfaceType + + +
    +

    Type indicates the type of the interface.

    +
    +mtu
    + +int32 + +
    +(Optional) +

    MTU (Maximum Transmission Unit) specifies the size of the largest packet that can be sent over the interface.

    +
    +switchport
    + + +Switchport + + +
    +(Optional) +

    Switchport defines the switchport configuration for the interface. +This is only applicable for interfaces that are switchports (e.g., Ethernet interfaces).

    +
    +ipv4Addresses
    + +[]string + +
    +(Optional) +

    Ipv4Addresses is the list of IPv4 addresses assigned to the interface. +Each address should be given either in CIDR notation (e.g., “10.0.0.132”) +or as interface reference in the form of “unnumbered:” (e.g., “unnumbered:lo0”).

    +
    +
    +status
    + + +InterfaceStatus + + +
    +

    Status of the resource. This is set and updated automatically. +Read-only. +More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    +
    +

    InterfaceSpec +

    +

    +(Appears on:Interface) +

    +
    +

    InterfaceSpec defines the desired state of Interface.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    Name is the name of the interface.

    +
    +adminState
    + + +AdminState + + +
    +

    AdminState indicates whether the interface is administratively up or down.

    +
    +description
    + +string + +
    +(Optional) +

    Description provides a human-readable description of the interface.

    +
    +type
    + + +InterfaceType + + +
    +

    Type indicates the type of the interface.

    +
    +mtu
    + +int32 + +
    +(Optional) +

    MTU (Maximum Transmission Unit) specifies the size of the largest packet that can be sent over the interface.

    +
    +switchport
    + + +Switchport + + +
    +(Optional) +

    Switchport defines the switchport configuration for the interface. +This is only applicable for interfaces that are switchports (e.g., Ethernet interfaces).

    +
    +ipv4Addresses
    + +[]string + +
    +(Optional) +

    Ipv4Addresses is the list of IPv4 addresses assigned to the interface. +Each address should be given either in CIDR notation (e.g., “10.0.0.132”) +or as interface reference in the form of “unnumbered:” (e.g., “unnumbered:lo0”).

    +
    +

    InterfaceStatus +

    +

    +(Appears on:Interface) +

    +
    +

    InterfaceStatus defines the observed state of Interface.

    +
    + + + + + + + + + + + + + +
    FieldDescription
    +conditions
    + + +[]Kubernetes meta/v1.Condition + + +
    +(Optional) +

    The conditions are a list of status objects that describe the state of the Interface.

    +
    +

    InterfaceType +(string alias)

    +

    +(Appears on:InterfaceSpec) +

    +
    +

    InterfaceType represents the type of the interface.

    +
    + + + + + + + + + + + + +
    ValueDescription

    "Loopback"

    InterfaceTypeLoopback indicates that the interface is a loopback interface.

    +

    "Physical"

    InterfaceTypePhysical indicates that the interface is a physical/ethernet interface.

    +
    +

    LogFacility +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    The name of the log facility.

    +
    +severity
    + + +Severity + + +
    +

    The severity level of the log messages for this facility.

    +
    +

    LogServer +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +address
    + +string + +
    +

    IP address or hostname of the remote log server

    +
    +severity
    + + +Severity + + +
    +

    The servity level of the log messages sent to the server.

    +
    +networkInstance
    + +string + +
    +

    The network instance used to reach the log server.

    +
    +port
    + +int64 + +
    +(Optional) +

    The destination port number for syslog UDP messages to +the server. The default is 514.

    +
    +

    Logging +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +servers
    + + +[]*./api/v1alpha1.LogServer + + +
    +

    Servers is a list of remote log servers to which the device will send logs.

    +
    +facilities
    + + +[]*./api/v1alpha1.LogFacility + + +
    +

    Facilities is a list of log facilities to configure on the device.

    +
    +

    NTP +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +srcIf
    + +string + +
    +

    Source interface for all NTP traffic.

    +
    +servers
    + + +[]*./api/v1alpha1.NTPServer + + +
    +

    NTP servers.

    +
    +

    NTPServer +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +address
    + +string + +
    +

    Hostname/IP address of the NTP server.

    +
    +prefer
    + +bool + +
    +(Optional) +

    Indicates whether this server should be preferred or not.

    +
    +networkInstance
    + +string + +
    +(Optional) +

    The network instance used to communicate with the NTP server.

    +
    +

    NameServer +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +address
    + +string + +
    +

    The Hostname or IP address of the DNS server.

    +
    +networkInstance
    + +string + +
    +(Optional) +

    The network instance used to communicate with the DNS server.

    +
    +

    PKI +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + +
    FieldDescription
    +certificates
    + + +[]*./api/v1alpha1.Certificate + + +
    +

    Certificates is a list of certificates to be managed by the PKI.

    +
    +

    PasswordSource +

    +

    +(Appears on:User) +

    +
    +

    PasswordSource represents a source for the value of a password.

    +
    + + + + + + + + + + + + + +
    FieldDescription
    +secretKeyRef
    + + +Kubernetes core/v1.SecretKeySelector + + +
    +

    Selects a key of a secret.

    +
    +

    SNMP +

    +

    +(Appears on:DeviceSpec) +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +contact
    + +string + +
    +

    The contact information for the SNMP server.

    +
    +location
    + +string + +
    +

    The location information for the SNMP server.

    +
    +engineId
    + +string + +
    +

    The SNMP engine ID for the SNMP server.

    +
    +srcIf
    + +string + +
    +

    Source interface to be used for sending out SNMP Trap/Inform notifications.

    +
    +communities
    + + +[]*./api/v1alpha1.SNMPCommunity + + +
    +(Optional) +

    SNMP communities for SNMPv1 or SNMPv2c.

    +
    +destinations
    + + +[]*./api/v1alpha1.SNMPDestination + + +
    +

    SNMP destinations for SNMP traps or informs.

    +
    +traps
    + +[]string + +
    +(Optional) +

    The list of trap groups to enable.

    +
    +

    SNMPCommunity +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +(Optional) +

    Name of the community.

    +
    +group
    + +string + +
    +(Optional) +

    Group to which the community belongs.

    +
    +acl
    + +string + +
    +(Optional) +

    ACL name to filter snmp requests.

    +
    +

    SNMPDestination +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +address
    + +string + +
    +

    The Hostname or IP address of the SNMP host to send notifications to.

    +
    +type
    + +string + +
    +(Optional) +

    Type of message to send to host. Default is traps.

    +
    +version
    + +string + +
    +(Optional) +

    SNMP version. Default is v2c.

    +
    +target
    + +string + +
    +(Optional) +

    SNMP community or user name.

    +
    +networkInstance
    + +string + +
    +(Optional) +

    The network instance to use to source traffic.

    +
    +

    Severity +(string alias)

    +

    +(Appears on:LogFacility, LogServer) +

    +
    +

    Severity represents the severity level of a log message.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    ValueDescription

    "Alert"

    "Critical"

    "Debug"

    "Emergency"

    "Error"

    "Info"

    "Notice"

    "Warning"

    +

    Switchport +

    +

    +(Appears on:InterfaceSpec) +

    +
    +

    Switchport defines the switchport configuration for an interface.

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +mode
    + + +SwitchportMode + + +
    +

    Mode defines the switchport mode, such as access or trunk.

    +
    +accessVlan
    + +int32 + +
    +(Optional) +

    AccessVlan specifies the VLAN ID for access mode switchports. +Only applicable when Mode is set to “Access”.

    +
    +nativeVlan
    + +int32 + +
    +(Optional) +

    NativeVlan specifies the native VLAN ID for trunk mode switchports. +Only applicable when Mode is set to “Trunk”.

    +
    +allowedVlans
    + +[]int32 + +
    +(Optional) +

    AllowedVlans is a list of VLAN IDs that are allowed on the trunk port. +Only applicable when Mode is set to “Trunk”.

    +
    +

    SwitchportMode +(string alias)

    +

    +(Appears on:Switchport) +

    +
    +

    SwitchportMode represents the switchport mode of an interface.

    +
    + + + + + + + + + + + + +
    ValueDescription

    "Access"

    SwitchportModeAccess indicates that the switchport is in access mode.

    +

    "Trunk"

    SwitchportModeTrunk indicates that the switchport is in trunk mode.

    +
    +

    TLS +

    +

    +(Appears on:Endpoint) +

    +
    +
    + + + + + + + + + + + + + + + + + +
    FieldDescription
    +ca
    + + +Kubernetes core/v1.SecretKeySelector + + +
    +

    The CA certificate to verify the server’s identity.

    +
    +certificate
    + + +CertificateSource + + +
    +(Optional) +

    The client certificate and private key to use for mutual TLS authentication. +Leave empty if mTLS is not desired.

    +
    +

    TemplateSource +

    +

    +(Appears on:Bootstrap, DeviceSpec) +

    +
    +

    TemplateSource defines a source for template content. +It can be provided inline, or as a reference to a Secret or ConfigMap.

    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +inline
    + +string + +
    +(Optional) +

    Inline template content

    +
    +secretRef
    + + +Kubernetes core/v1.SecretKeySelector + + +
    +(Optional) +

    Reference to a Secret containing the template

    +
    +configMapRef
    + + +Kubernetes core/v1.ConfigMapKeySelector + + +
    +(Optional) +

    Reference to a ConfigMap containing the template

    +
    +

    User +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    Assigned username for this user.

    +
    +password
    + + +PasswordSource + + +
    +

    The user password, supplied as cleartext.

    +
    +role
    + +string + +
    +

    Role which the user is to be assigned to.

    +
    +
    +

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

    diff --git a/go.mod b/go.mod index 1d56a098..34bb2b71 100644 --- a/go.mod +++ b/go.mod @@ -14,8 +14,6 @@ require ( github.com/openconfig/goyang v1.6.2 github.com/openconfig/ygnmi v0.13.1-0.20250924235719-646562b5d0c3 github.com/openconfig/ygot v0.32.0 - github.com/sapcc/go-api-declarations v1.16.0 - go.uber.org/automaxprocs v1.6.0 go.uber.org/zap v1.27.0 google.golang.org/grpc v1.71.0 google.golang.org/protobuf v1.36.5 diff --git a/go.sum b/go.sum index 2bcc6351..3fb00d76 100644 --- a/go.sum +++ b/go.sum @@ -124,8 +124,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -137,8 +135,6 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sapcc/go-api-declarations v1.16.0 h1:uMB6SFPBAClusbAWwmeMeUi5eHVf9FpZWWf3VwPrsIg= -github.com/sapcc/go-api-declarations v1.16.0/go.mod h1:MWmLjmvjftgyAugNUfIhsDsHIzXH1pn32cWLZpiluKg= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= @@ -178,8 +174,6 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/hack/api-reference/config.json b/hack/api-reference/config.json new file mode 100644 index 00000000..cf230e1c --- /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.32/#{{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/license-header.txt b/hack/license-header.txt new file mode 100644 index 00000000..3ceae8e8 --- /dev/null +++ b/hack/license-header.txt @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/hack/validate-kustomize.sh b/hack/validate-kustomize.sh new file mode 100755 index 00000000..41023e5a --- /dev/null +++ b/hack/validate-kustomize.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -e + +BASEDIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /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 diff --git a/internal/provider/cisco/nxos/api/nxapi_test.go b/internal/provider/cisco/nxos/api/nxapi_test.go index 250183d4..6441b58a 100644 --- a/internal/provider/cisco/nxos/api/nxapi_test.go +++ b/internal/provider/cisco/nxos/api/nxapi_test.go @@ -13,6 +13,8 @@ import ( "github.com/ironcore-dev/network-operator/internal/provider/cisco/nxos/gnmiext" ) +const nxapiItems = "System/fm-items/nxapi-items" + func Test_NXAPI(t *testing.T) { nxapi := &NXAPI{ Enable: true, @@ -31,8 +33,8 @@ func Test_NXAPI(t *testing.T) { t.Errorf("expected value to be of type EditingUpdate") } - if update.XPath != "System/fm-items/nxapi-items" { - t.Errorf("expected key 'System/fm-items/nxapi-items' to be present") + if update.XPath != nxapiItems { + t.Errorf("expected key '" + nxapiItems + "' to be present") } i, ok := update.Value.(*nxos.Cisco_NX_OSDevice_System_FmItems_NxapiItems) @@ -64,8 +66,8 @@ func Test_NXAPI_Trustpoint(t *testing.T) { t.Errorf("expected value to be of type EditingUpdate") } - if update.XPath != "System/fm-items/nxapi-items" { - t.Errorf("expected key 'System/fm-items/nxapi-items' to be present") + if update.XPath != nxapiItems { + t.Errorf("expected key '" + nxapiItems + "' to be present") } i, ok := update.Value.(*nxos.Cisco_NX_OSDevice_System_FmItems_NxapiItems) @@ -122,8 +124,8 @@ func Test_NXAPI_Cert(t *testing.T) { t.Errorf("expected value to be of type EditingUpdate") } - if update.XPath != "System/fm-items/nxapi-items" { - t.Errorf("expected key 'System/fm-items/nxapi-items' to be present") + if update.XPath != nxapiItems { + t.Errorf("expected key '" + nxapiItems + "' to be present") } i, ok := update.Value.(*nxos.Cisco_NX_OSDevice_System_FmItems_NxapiItems) @@ -176,8 +178,8 @@ func Test_NXAPI_Disabled(t *testing.T) { t.Errorf("expected value to be of type EditingUpdate") } - if update.XPath != "System/fm-items/nxapi-items" { - t.Errorf("expected key 'System/fm-items/nxapi-items' to be present") + if update.XPath != nxapiItems { + t.Errorf("expected key '" + nxapiItems + "' to be present") } i, ok := update.Value.(*nxos.Cisco_NX_OSDevice_System_FmItems_NxapiItems) diff --git a/internal/provider/cisco/nxos/provider.go b/internal/provider/cisco/nxos/provider.go index 8f7efc83..ddf1aaea 100644 --- a/internal/provider/cisco/nxos/provider.go +++ b/internal/provider/cisco/nxos/provider.go @@ -108,7 +108,12 @@ func (p *Provider) CreateDevice(ctx context.Context, device *v1alpha1.Device) er if err != nil { return fmt.Errorf("failed to create grpc connection: %w", err) } - defer conn.Close() + defer func(conn *grpc.ClientConn) { + err := conn.Close() + if err != nil { + log.Error(err, "failed to close grpc connection") + } + }(conn) var opts []gnmiext.Option var isDryRun bool diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index fb8d2330..dcb7aa4d 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -12,30 +12,27 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/ironcore-dev/network-operator/test/util" + "github.com/ironcore-dev/network-operator/test/utils" ) var ( // Optional Environment Variables: - // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. - // These variables are useful if Prometheus or CertManager is already installed, avoiding re-installation and conflicts. - skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + // These variables are useful if CertManager is already installed, avoiding + // re-installation and conflicts. skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" - // isPrometheusOperatorAlreadyInstalled will be set true when prometheus CRDs be found on the cluster - isPrometheusOperatorAlreadyInstalled = false // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster isCertManagerAlreadyInstalled = false -) -// image is the name of the image which will be build and loaded -// with the code source changes to be tested. -const image = "ironcore.dev/network-operator:test" + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/network-operator:v0.0.1" +) // TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, -// temporary environment to validate project changes with the purposed to be used in CI jobs. +// temporary environment to validate project changes with the purpose of being used in CI jobs. // The default setup requires Kind, builds/loads the Manager Docker image locally, and installs -// CertManager and Prometheus Operator. +// CertManager. func TestE2E(t *testing.T) { RegisterFailHandler(Fail) _, _ = fmt.Fprintf(GinkgoWriter, "Starting network-operator integration test suite\n") @@ -43,42 +40,27 @@ func TestE2E(t *testing.T) { } var _ = BeforeSuite(func() { - By("Ensure that Prometheus is enabled") - cwd, err := util.GetProjectDir() - Expect(err).NotTo(HaveOccurred(), "Failed to get project directory") - - err = util.UncommentCode(cwd+"/config/default/kustomization.yaml", "#- ../prometheus", "#") - Expect(err).NotTo(HaveOccurred(), "Failed to enable Prometheus") - By("building the manager(Operator) image") - cmd := exec.Command("make", "docker-build", "IMG="+image) - _, err = util.Run(cmd) + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. By("loading the manager(Operator) image on Kind") - err = util.LoadImageToKindClusterWithName(image) + err = utils.LoadImageToKindClusterWithName(projectImage) ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing. - // To prevent errors when tests run in environments with Prometheus or CertManager already installed, - // we check for their presence before execution. - // Setup Prometheus and CertManager before the suite if not skipped and if not already installed - if !skipPrometheusInstall { - By("checking if prometheus is installed already") - isPrometheusOperatorAlreadyInstalled = util.IsPrometheusCRDsInstalled() - if !isPrometheusOperatorAlreadyInstalled { - _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") - Expect(util.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") - } else { - _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: Prometheus Operator is already installed. Skipping installation...\n") - } - } + // To prevent errors when tests run in environments with CertManager already installed, + // we check for its presence before execution. + // Setup CertManager before the suite if not skipped and if not already installed if !skipCertManagerInstall { By("checking if cert manager is installed already") - isCertManagerAlreadyInstalled = util.IsCertManagerCRDsInstalled() + isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled() if !isCertManagerAlreadyInstalled { _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") - Expect(util.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") } else { _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n") } @@ -86,13 +68,9 @@ var _ = BeforeSuite(func() { }) var _ = AfterSuite(func() { - // Teardown Prometheus and CertManager after the suite if not skipped and if they were not already installed - if !skipPrometheusInstall && !isPrometheusOperatorAlreadyInstalled { - _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") - util.UninstallPrometheusOperator() - } + // Teardown CertManager after the suite if not skipped and if it was not already installed if !skipCertManagerInstall && !isCertManagerAlreadyInstalled { _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") - util.UninstallCertManager() + utils.UninstallCertManager() } }) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 85ad485e..39ce2f14 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -14,7 +14,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/ironcore-dev/network-operator/test/util" + "github.com/ironcore-dev/network-operator/test/utils" ) // namespace where the project is deployed in @@ -38,61 +38,54 @@ var _ = Describe("Manager", Ordered, func() { BeforeAll(func() { By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) - _, err := util.Run(cmd) + _, err := utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to create namespace") By("labeling the namespace to enforce the restricted security policy") - cmd = exec.Command("kubectl", "label", "--overwrite", "ns", namespace, "pod-security.kubernetes.io/enforce=restricted") - _, err = util.Run(cmd) + cmd = exec.Command("kubectl", "label", "--overwrite", "ns", namespace, + "pod-security.kubernetes.io/enforce=restricted") + _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") By("installing CRDs") - cmd = exec.Command("make", "deploy-crds") - _, err = util.Run(cmd) + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs") By("deploying the controller-manager") - cmd = exec.Command("make", "deploy", "IMG="+image) - _, err = util.Run(cmd) + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") }) // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, // and deleting the namespace. AfterAll(func() { - By("cleaning up the ClusterRoleBinding of the service account to allow access to metrics") - cmd := exec.Command("kubectl", "delete", "clusterrolebinding", metricsRoleBindingName) - _, err := util.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to delete ClusterRoleBinding") - By("cleaning up the curl pod for metrics") - cmd = exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace) - _, err = util.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to delete curl-metrics pod") + cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace) + _, _ = utils.Run(cmd) By("undeploying the controller-manager") cmd = exec.Command("make", "undeploy") - _, err = util.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to undeploy the controller-manager") + _, _ = utils.Run(cmd) By("uninstalling CRDs") - cmd = exec.Command("make", "undeploy-crds") - _, err = util.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to uninstall CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) By("removing manager namespace") - cmd = exec.Command("kubectl", "delete", "ns", namespace, "--ignore-not-found") - _, err = util.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "Failed to delete namespace") + cmd = exec.Command("kubectl", "delete", "ns", namespace) + _, _ = utils.Run(cmd) }) // After each test, check for failures and collect logs, events, // and pod descriptions for debugging. AfterEach(func() { - if specReport := CurrentSpecReport(); specReport.Failed() { + specReport := CurrentSpecReport() + if specReport.Failed() { By("Fetching controller manager pod logs") cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) - controllerLogs, err := util.Run(cmd) + controllerLogs, err := utils.Run(cmd) if err == nil { _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) } else { @@ -101,7 +94,7 @@ var _ = Describe("Manager", Ordered, func() { By("Fetching Kubernetes events") cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") - eventsOutput, err := util.Run(cmd) + eventsOutput, err := utils.Run(cmd) if err == nil { _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) } else { @@ -110,7 +103,7 @@ var _ = Describe("Manager", Ordered, func() { By("Fetching curl-metrics logs") cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) - metricsOutput, err := util.Run(cmd) + metricsOutput, err := utils.Run(cmd) if err == nil { _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) } else { @@ -119,7 +112,7 @@ var _ = Describe("Manager", Ordered, func() { By("Fetching controller manager pod description") cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace) - podDescription, err := util.Run(cmd) + podDescription, err := utils.Run(cmd) if err == nil { fmt.Println("Pod description:\n", podDescription) } else { @@ -145,16 +138,19 @@ var _ = Describe("Manager", Ordered, func() { "-n", namespace, ) - podOutput, err := util.Run(cmd) + podOutput, err := utils.Run(cmd) g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") - podNames := util.GetNonEmptyLines(podOutput) + podNames := utils.GetNonEmptyLines(podOutput) g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running") controllerPodName = podNames[0] g.Expect(controllerPodName).To(ContainSubstring("controller-manager")) // Validate the pod's status - cmd = exec.Command("kubectl", "get", "pods", controllerPodName, "-o", "jsonpath={.status.phase}", "-n", namespace) - output, err := util.Run(cmd) + cmd = exec.Command("kubectl", "get", + "pods", controllerPodName, "-o", "jsonpath={.status.phase}", + "-n", namespace, + ) + output, err := utils.Run(cmd) g.Expect(err).NotTo(HaveOccurred()) g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status") } @@ -163,21 +159,18 @@ var _ = Describe("Manager", Ordered, func() { It("should ensure the metrics endpoint is serving metrics", func() { By("creating a ClusterRoleBinding for the service account to allow access to metrics") - // #nosec G204 - cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, "--clusterrole=network-operator-metrics-reader", fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName)) - _, err := util.Run(cmd) + cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, + "--clusterrole=network-operator-metrics-reader", + fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName), + ) + _, err := utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding") By("validating that the metrics service is available") cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace) - _, err = util.Run(cmd) + _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Metrics service should exist") - By("validating that the ServiceMonitor for Prometheus is applied in the namespace") - cmd = exec.Command("kubectl", "get", "ServiceMonitor", "-n", namespace) - _, err = util.Run(cmd) - Expect(err).NotTo(HaveOccurred(), "ServiceMonitor should exist") - By("getting the service account token") token, err := serviceAccountToken() Expect(err).NotTo(HaveOccurred()) @@ -185,8 +178,8 @@ var _ = Describe("Manager", Ordered, func() { By("waiting for the metrics endpoint to be ready") verifyMetricsEndpointReady := func(g Gomega) { - kcmd := exec.Command("kubectl", "get", "endpoints", metricsServiceName, "-n", namespace) - output, err := util.Run(kcmd) + cmd := exec.Command("kubectl", "get", "endpoints", metricsServiceName, "-n", namespace) + output, err := utils.Run(cmd) g.Expect(err).NotTo(HaveOccurred()) g.Expect(output).To(ContainSubstring("8443"), "Metrics endpoint is not ready") } @@ -194,15 +187,15 @@ var _ = Describe("Manager", Ordered, func() { By("verifying that the controller manager is serving the metrics server") verifyMetricsServerStarted := func(g Gomega) { - kcmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) - output, err := util.Run(kcmd) + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + output, err := utils.Run(cmd) g.Expect(err).NotTo(HaveOccurred()) - g.Expect(output).To(ContainSubstring("controller-runtime.metrics\tServing metrics server"), "Metrics server not yet started") + g.Expect(output).To(ContainSubstring("controller-runtime.metrics\tServing metrics server"), + "Metrics server not yet started") } Eventually(verifyMetricsServerStarted).Should(Succeed()) By("creating the curl-metrics pod to access the metrics endpoint") - // #nosec G204 cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", "--namespace", namespace, "--image=curlimages/curl:latest", @@ -215,6 +208,7 @@ var _ = Describe("Manager", Ordered, func() { "command": ["/bin/sh", "-c"], "args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"], "securityContext": { + "readOnlyRootFilesystem": true, "allowPrivilegeEscalation": false, "capabilities": { "drop": ["ALL"] @@ -226,16 +220,18 @@ var _ = Describe("Manager", Ordered, func() { } } }], - "serviceAccount": "%s" + "serviceAccountName": "%s" } }`, token, metricsServiceName, namespace, serviceAccountName)) - _, err = util.Run(cmd) + _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") By("waiting for the curl-metrics pod to complete.") verifyCurlUp := func(g Gomega) { - cmd := exec.Command("kubectl", "get", "pods", "curl-metrics", "-o", "jsonpath={.status.phase}", "-n", namespace) - output, err := util.Run(cmd) + cmd := exec.Command("kubectl", "get", "pods", "curl-metrics", + "-o", "jsonpath={.status.phase}", + "-n", namespace) + output, err := utils.Run(cmd) g.Expect(err).NotTo(HaveOccurred()) g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status") } @@ -243,7 +239,9 @@ var _ = Describe("Manager", Ordered, func() { By("getting the metrics by checking curl-metrics logs") metricsOutput := getMetricsOutput() - Expect(metricsOutput).To(ContainSubstring("controller_runtime_webhook_panics_total")) + Expect(metricsOutput).To(ContainSubstring( + "controller_runtime_reconcile_total", + )) }) // +kubebuilder:scaffold:e2e-webhooks-checks @@ -263,14 +261,13 @@ var _ = Describe("Manager", Ordered, func() { // It uses the Kubernetes TokenRequest API to generate a token by directly sending a request // and parsing the resulting token from the API response. func serviceAccountToken() (string, error) { - // #nosec G101 const tokenRequestRawString = `{ "apiVersion": "authentication.k8s.io/v1", "kind": "TokenRequest" }` // Temporary file to store the token request - secretName := serviceAccountName + "-token-request" + secretName := fmt.Sprintf("%s-token-request", serviceAccountName) tokenRequestFile := filepath.Join("/tmp", secretName) err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644)) if err != nil { @@ -280,8 +277,12 @@ func serviceAccountToken() (string, error) { var out string verifyTokenCreation := func(g Gomega) { // Execute kubectl command to create the token - // #nosec G204 - cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts/%s/token", namespace, serviceAccountName), "-f", tokenRequestFile) + cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf( + "/api/v1/namespaces/%s/serviceaccounts/%s/token", + namespace, + serviceAccountName, + ), "-f", tokenRequestFile) + output, err := cmd.CombinedOutput() g.Expect(err).NotTo(HaveOccurred()) @@ -301,7 +302,7 @@ func serviceAccountToken() (string, error) { func getMetricsOutput() string { By("getting the curl-metrics logs") cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) - metricsOutput, err := util.Run(cmd) + metricsOutput, err := utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod") Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK")) return metricsOutput diff --git a/test/gnmi/main.go b/test/gnmi/main.go index 024c5eef..81abf218 100644 --- a/test/gnmi/main.go +++ b/test/gnmi/main.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + package main import ( diff --git a/test/util/util.go b/test/utils/util.go similarity index 64% rename from test/util/util.go rename to test/utils/util.go index 174c09ea..7e6fe073 100644 --- a/test/util/util.go +++ b/test/utils/util.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors // SPDX-License-Identifier: Apache-2.0 -package util +package utils import ( "bufio" @@ -11,12 +11,16 @@ import ( "os/exec" "strings" - . "github.com/onsi/ginkgo/v2" //nolint:golint,revive + . "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck ) const ( - prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.82.2/bundle.yaml" - certmanagerURL = "https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml" + prometheusOperatorVersion = "v0.77.1" + prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + + "releases/download/%s/bundle.yaml" + + certmanagerVersion = "v1.16.3" + certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml" ) func warnError(err error) { @@ -25,22 +29,19 @@ func warnError(err error) { // Run executes the provided command within this context func Run(cmd *exec.Cmd) (string, error) { - dir, err := GetProjectDir() - if err != nil { - return "", fmt.Errorf("failed to get project directory: %w", err) - } - + dir, _ := GetProjectDir() cmd.Dir = dir - if err = os.Chdir(cmd.Dir); err != nil { - _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err) + + if err := os.Chdir(cmd.Dir); err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err) } + cmd.Env = append(os.Environ(), "GO111MODULE=on") command := strings.Join(cmd.Args, " ") - _, _ = fmt.Fprintf(GinkgoWriter, "running: %s\n", command) - + _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command) output, err := cmd.CombinedOutput() if err != nil { - return string(output), fmt.Errorf("%s failed with error: (%w) %s", command, err, string(output)) + return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err) } return string(output), nil @@ -48,14 +49,16 @@ func Run(cmd *exec.Cmd) (string, error) { // InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. func InstallPrometheusOperator() error { - cmd := exec.Command("kubectl", "create", "-f", prometheusOperatorURL) + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "create", "-f", url) _, err := Run(cmd) return err } // UninstallPrometheusOperator uninstalls the prometheus func UninstallPrometheusOperator() { - cmd := exec.Command("kubectl", "delete", "-f", prometheusOperatorURL) + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) if _, err := Run(cmd); err != nil { warnError(err) } @@ -90,15 +93,30 @@ func IsPrometheusCRDsInstalled() bool { // UninstallCertManager uninstalls the cert manager func UninstallCertManager() { - cmd := exec.Command("kubectl", "delete", "-f", certmanagerURL) + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) if _, err := Run(cmd); err != nil { warnError(err) } + + // Delete leftover leases in kube-system (not cleaned by default) + kubeSystemLeases := []string{ + "cert-manager-cainjector-leader-election", + "cert-manager-controller", + } + for _, lease := range kubeSystemLeases { + cmd = exec.Command("kubectl", "delete", "lease", lease, + "-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0") + if _, err := Run(cmd); err != nil { + warnError(err) + } + } } // InstallCertManager installs the cert manager bundle. func InstallCertManager() error { - cmd := exec.Command("kubectl", "apply", "-f", certmanagerURL) + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "apply", "-f", url) if _, err := Run(cmd); err != nil { return err } @@ -153,31 +171,8 @@ func LoadImageToKindClusterWithName(name string) error { if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { cluster = v } - // See: https://kind.sigs.k8s.io/docs/user/rootless/#creating-a-kind-cluster-with-rootless-nerdctl - prov, ok := os.LookupEnv("KIND_EXPERIMENTAL_PROVIDER") - if ok && prov != "docker" { - // If kind is configured to not use the docker runtime (e.g. when using podman or nerctl), - // we need to create a temp file to store the image archive and load it as a tarball. - // See: https://github.com/kubernetes-sigs/kind/issues/2760 - file, err := os.CreateTemp("", "operator-image-") - if err != nil { - return fmt.Errorf("failed to create temp file: %w", err) - } - _ = file.Close() - defer func() { _ = os.Remove(file.Name()) }() - - // https://github.com/containerd/nerdctl/blob/main/docs/command-reference.md#whale-nerdctl-save - // https://docs.podman.io/en/v5.3.0/markdown/podman-save.1.html - cmd := exec.Command(prov, "save", name, "--output", file.Name()) - if _, err = Run(cmd); err != nil { - return fmt.Errorf("failed to save image: %w", err) - } - - cmd = exec.Command("kind", "load", "image-archive", file.Name(), "--name", cluster) //nolint:gosec - _, err = Run(cmd) - return err - } - cmd := exec.Command("kind", "load", "docker-image", name, "--name", cluster) + kindOptions := []string{"load", "docker-image", name, "--name", cluster} + cmd := exec.Command("kind", kindOptions...) _, err := Run(cmd) return err } @@ -192,6 +187,7 @@ func GetNonEmptyLines(output string) []string { res = append(res, element) } } + return res } @@ -199,7 +195,7 @@ func GetNonEmptyLines(output string) []string { func GetProjectDir() (string, error) { wd, err := os.Getwd() if err != nil { - return wd, err + return wd, fmt.Errorf("failed to get current working directory: %w", err) } wd = strings.ReplaceAll(wd, "/test/e2e", "") return wd, nil @@ -208,23 +204,23 @@ func GetProjectDir() (string, error) { // UncommentCode searches for target in the file and remove the comment prefix // of the target content. The target content may span multiple lines. func UncommentCode(filename, target, prefix string) error { + // false positive + // nolint:gosec content, err := os.ReadFile(filename) if err != nil { - return err + return fmt.Errorf("failed to read file %q: %w", filename, err) } + strContent := string(content) - idx := strings.Index(string(content), target) + idx := strings.Index(strContent, target) if idx < 0 { - if strings.Contains(string(content), target[len(prefix):]) { - return nil // already uncommented - } - - return fmt.Errorf("unable to find the code %s to be uncomment", target) + return fmt.Errorf("unable to find the code %q to be uncomment", target) } out := new(bytes.Buffer) - if _, err = out.Write(content[:idx]); err != nil { - return err + _, err = out.Write(content[:idx]) + if err != nil { + return fmt.Errorf("failed to write to output: %w", err) } scanner := bufio.NewScanner(bytes.NewBufferString(target)) @@ -232,22 +228,27 @@ func UncommentCode(filename, target, prefix string) error { return nil } for { - _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)) - if err != nil { - return err + if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil { + return fmt.Errorf("failed to write to output: %w", err) } // Avoid writing a newline in case the previous line was the last in target. if !scanner.Scan() { break } if _, err = out.WriteString("\n"); err != nil { - return err + return fmt.Errorf("failed to write to output: %w", err) } } if _, err = out.Write(content[idx+len(target):]); err != nil { - return err + return fmt.Errorf("failed to write to output: %w", err) + } + + // false positive + // nolint:gosec + if err = os.WriteFile(filename, out.Bytes(), 0o644); err != nil { + return fmt.Errorf("failed to write file %q: %w", filename, err) } - return os.WriteFile(filename, out.Bytes(), 0o644) + return nil } From 09c3aefbb1d3fce14512b54a647d0615cf8b4989 Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Wed, 27 Aug 2025 11:55:53 +0200 Subject: [PATCH 2/5] PR review --- .github/workflows/publish-chart.yaml | 10 +- .github/workflows/test-chart.yml | 4 +- .golangci.yml | 51 +- Makefile | 9 + charts/network-operator/Chart.lock | 6 - charts/network-operator/Chart.yaml | 5 - .../crd/networking.cloud.sap_devices.yaml | 164 ++-- .../crd/networking.cloud.sap_interfaces.yaml | 3 + .../network-policy/allow-metrics-traffic.yaml | 6 +- .../network-operator/templates/rbac/role.yaml | 10 +- charts/network-operator/values.yaml | 16 +- cmd/main.go | 5 + dist/chart/.helmignore | 25 - dist/chart/Chart.yaml | 7 - dist/chart/templates/_helpers.tpl | 50 -- .../templates/certmanager/certificate.yaml | 60 -- .../crd/networking.cloud.sap_devices.yaml | 777 ------------------ .../crd/networking.cloud.sap_interfaces.yaml | 241 ------ dist/chart/templates/manager/manager.yaml | 71 -- .../templates/metrics/metrics-service.yaml | 17 - .../network-policy/allow-metrics-traffic.yaml | 28 - dist/chart/templates/prometheus/monitor.yaml | 39 - .../templates/rbac/device_admin_role.yaml | 28 - .../templates/rbac/device_editor_role.yaml | 34 - .../templates/rbac/device_viewer_role.yaml | 30 - .../templates/rbac/interface_admin_role.yaml | 28 - .../templates/rbac/interface_editor_role.yaml | 34 - .../templates/rbac/interface_viewer_role.yaml | 30 - .../templates/rbac/leader_election_role.yaml | 42 - .../rbac/leader_election_role_binding.yaml | 17 - .../templates/rbac/metrics_auth_role.yaml | 21 - .../rbac/metrics_auth_role_binding.yaml | 16 - .../templates/rbac/metrics_reader_role.yaml | 13 - dist/chart/templates/rbac/role.yaml | 63 -- dist/chart/templates/rbac/role_binding.yaml | 16 - .../chart/templates/rbac/service_account.yaml | 15 - dist/chart/values.yaml | 76 -- go.mod | 1 + go.sum | 4 + 39 files changed, 176 insertions(+), 1896 deletions(-) delete mode 100644 charts/network-operator/Chart.lock delete mode 100644 dist/chart/.helmignore delete mode 100644 dist/chart/Chart.yaml delete mode 100644 dist/chart/templates/_helpers.tpl delete mode 100644 dist/chart/templates/certmanager/certificate.yaml delete mode 100755 dist/chart/templates/crd/networking.cloud.sap_devices.yaml delete mode 100755 dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml delete mode 100644 dist/chart/templates/manager/manager.yaml delete mode 100644 dist/chart/templates/metrics/metrics-service.yaml delete mode 100755 dist/chart/templates/network-policy/allow-metrics-traffic.yaml delete mode 100644 dist/chart/templates/prometheus/monitor.yaml delete mode 100755 dist/chart/templates/rbac/device_admin_role.yaml delete mode 100755 dist/chart/templates/rbac/device_editor_role.yaml delete mode 100755 dist/chart/templates/rbac/device_viewer_role.yaml delete mode 100755 dist/chart/templates/rbac/interface_admin_role.yaml delete mode 100755 dist/chart/templates/rbac/interface_editor_role.yaml delete mode 100755 dist/chart/templates/rbac/interface_viewer_role.yaml delete mode 100755 dist/chart/templates/rbac/leader_election_role.yaml delete mode 100755 dist/chart/templates/rbac/leader_election_role_binding.yaml delete mode 100755 dist/chart/templates/rbac/metrics_auth_role.yaml delete mode 100755 dist/chart/templates/rbac/metrics_auth_role_binding.yaml delete mode 100755 dist/chart/templates/rbac/metrics_reader_role.yaml delete mode 100755 dist/chart/templates/rbac/role.yaml delete mode 100755 dist/chart/templates/rbac/role_binding.yaml delete mode 100755 dist/chart/templates/rbac/service_account.yaml delete mode 100644 dist/chart/values.yaml diff --git a/.github/workflows/publish-chart.yaml b/.github/workflows/publish-chart.yaml index ca9ae2ca..b4d6a726 100644 --- a/.github/workflows/publish-chart.yaml +++ b/.github/workflows/publish-chart.yaml @@ -63,17 +63,17 @@ jobs: - name: Package Helm chart with crds folder in template run: | - helm package dist/chart --version ${{ steps.chart_version.outputs.version }}-crds + helm package charts/network-operator --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 + mkdir -p charts/network-operator/crds + kustomize build config/default | yq ea 'select(.kind == "CustomResourceDefinition")' > charts/network-operator/crds/crds.yaml + rm -rf charts/network-operator/templates/crd - name: Package Helm chart with removed crds folder from template folder run: | - helm package dist/chart --version ${{ steps.chart_version.outputs.version }} + helm package charts/network-operator --version ${{ steps.chart_version.outputs.version }} - name: Log in to GitHub Container Registry run: | diff --git a/.github/workflows/test-chart.yml b/.github/workflows/test-chart.yml index 0fc2590a..60e32365 100644 --- a/.github/workflows/test-chart.yml +++ b/.github/workflows/test-chart.yml @@ -28,7 +28,7 @@ jobs: - name: Lint Helm Chart run: | - helm lint ./dist/chart + helm lint ./charts/network-operator - name: Create k8s Kind Cluster uses: helm/kind-action@v1 @@ -55,7 +55,7 @@ jobs: - name: Install Helm chart for project run: | - helm install my-release ./dist/chart --create-namespace --namespace network-operator-system + helm install my-release ./charts/network-operator --create-namespace --namespace network-operator-system - name: Check Helm release status run: | diff --git a/.golangci.yml b/.golangci.yml index c76afe02..6c99fa10 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,23 +5,39 @@ run: linters: default: none enable: + - bodyclose + - containedctx - copyloopvar - - dupl + - dupword + - durationcheck - errcheck + - errname + - errorlint + - exptostd + - forbidigo - ginkgolinter + - gocheckcompilerdirectives - goconst - - gocyclo + - gocritic + - gosec - govet - ineffassign - - lll + - intrange - misspell - - nakedret - - prealloc - - revive + - nilerr + - nolintlint + - nosprintfhostport + - perfsprint + - predeclared + - rowserrcheck + - sqlclosecheck - staticcheck - unconvert - unparam - unused + - usestdlibvars + - usetesting + - whitespace settings: revive: rules: @@ -30,6 +46,12 @@ linters: exclusions: generated: lax rules: + - linters: + - perfsprint + - nolintlint + - gosec + - forbidigo + path: test/ - linters: - lll path: api/* @@ -45,6 +67,8 @@ linters: path: test - linters: - lll + - gosec + - forbidigo path: hack/provider - linters: - gocyclo @@ -55,6 +79,12 @@ linters: path: internal/provider/cisco/nxos - linters: - lll + - perfsprint + - gosec + - gocritic + - whitespace + - errorlint + - dupword path: internal/provider/openconfig - linters: - staticcheck @@ -64,6 +94,10 @@ linters: - gofmt - gocyclo - dupl + - gocritic + - whitespace + - perfsprint + - errorlint path: internal/provider/cisco/nxos/genyang - linters: - staticcheck @@ -81,6 +115,11 @@ formatters: enable: - gofmt - goimports + settings: + goimports: + # Put local imports after 3rd-party packages + local-prefixes: + - github.com/ironcore-dev/network-operator exclusions: generated: lax paths: diff --git a/Makefile b/Makefile index 23afced5..9e1ba694 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # Image URL to use all building/pushing image targets IMG ?= controller:latest +TEST_SERVER_IMG ?= gnmi-test-server:latest # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -135,6 +136,7 @@ run: manifests generate fmt vet ## Run a controller from your host. .PHONY: helm helm: manifests kubebuilder $(KUBEBUILDER) edit --plugins=helm/v1-alpha + @rm -rf charts/network-operator && mv dist/chart charts/network-operator && rm -rf dist # 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. @@ -170,6 +172,13 @@ build-installer: manifests generate kustomize ## Generate a consolidated YAML wi cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/default > dist/install.yaml +.PHONY: build-test-gnmi-server +build-test-gnmi-server: ## Build docker image with the gnmi test server. + $(CONTAINER_TOOL) build -t ${TEST_SERVER_IMG} ./test/gnmi/ + +run-test-gnmi-server: build-test-gnmi-server + @$(CONTAINER_TOOL) run --rm -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG) + ##@ Deployment ifndef ignore-not-found diff --git a/charts/network-operator/Chart.lock b/charts/network-operator/Chart.lock deleted file mode 100644 index d655cac4..00000000 --- a/charts/network-operator/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: owner-info - repository: oci://ghcr.io/sapcc/helm-charts - version: 1.0.0 -digest: sha256:7643f231cc4ebda347fd12ec62fe4445c280e2b71d27eec555f3025290f5038f -generated: "2025-08-07T13:01:53.383695+02:00" diff --git a/charts/network-operator/Chart.yaml b/charts/network-operator/Chart.yaml index 2cd7c961..5eb1a44f 100644 --- a/charts/network-operator/Chart.yaml +++ b/charts/network-operator/Chart.yaml @@ -5,8 +5,3 @@ type: application version: 0.1.0 appVersion: "0.1.0" icon: "https://example.com/icon.png" -dependencies: - # See: https://github.com/sapcc/helm-charts/pkgs/container/helm-charts%2Fowner-info - - name: owner-info - repository: oci://ghcr.io/sapcc/helm-charts - version: 1.0.0 diff --git a/charts/network-operator/templates/crd/networking.cloud.sap_devices.yaml b/charts/network-operator/templates/crd/networking.cloud.sap_devices.yaml index 978598d1..d74f4dd9 100755 --- a/charts/network-operator/templates/crd/networking.cloud.sap_devices.yaml +++ b/charts/network-operator/templates/crd/networking.cloud.sap_devices.yaml @@ -21,15 +21,18 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - jsonPath: .spec.endpoint + - jsonPath: .spec.endpoint.address name: Endpoint type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - jsonPath: .status.phase name: Phase type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1alpha1 schema: openAPIV3Schema: @@ -278,10 +281,86 @@ spec: - domain type: object endpoint: - description: Endpoint is the management address of the device provided - as . - pattern: ^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$ - type: string + description: Endpoint contains the connection information for the + device. + properties: + address: + description: Address is the management address of the device provided + as . + pattern: ^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$ + type: string + secretRef: + description: |- + SecretRef is name of the authentication secret for the device containing the username and password. + The secret must be of type kubernetes.io/basic-auth and as such contain the following keys: 'username' and 'password'. + properties: + name: + description: name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: namespace defines the space within which the + secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + tls: + description: Transport credentials for grpc connection to the + switch. + properties: + ca: + description: The CA certificate to verify the server's identity. + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + certificate: + description: |- + The client certificate and private key to use for mutual TLS authentication. + Leave empty if mTLS is not desired. + properties: + secretRef: + description: |- + Secret containing the certificate. + The secret must be of type kubernetes.io/tls and as such contain the following keys: 'tls.crt' and 'tls.key'. + properties: + name: + description: name is unique within a namespace to + reference a secret resource. + type: string + namespace: + description: namespace defines the space within which + the secret name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + required: + - secretRef + type: object + required: + - ca + type: object + required: + - address + type: object grpc: description: |- Configuration for the gRPC server on the device. @@ -484,21 +563,6 @@ spec: required: - certificates type: object - secretRef: - description: |- - SecretRef is name of the authentication secret for the device containing the username and password. - The secret must be of type kubernetes.io/basic-auth and as such contain the following keys: 'username' and 'password'. - properties: - name: - description: name is unique within a namespace to reference a - secret resource. - type: string - namespace: - description: namespace defines the space within which the secret - name must be unique. - type: string - type: object - x-kubernetes-map-type: atomic snmp: description: SNMP global configuration. properties: @@ -577,59 +641,6 @@ spec: - location - srcIf type: object - tls: - description: Transport credentials for grpc connection to the switch. - properties: - ca: - description: The CA certificate to verify the server's identity. - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - certificate: - description: |- - The client certificate and private key to use for mutual TLS authentication. - Leave empty if mTLS is not desired. - properties: - secretRef: - description: |- - Secret containing the certificate. - The secret must be of type kubernetes.io/tls and as such contain the following keys: 'tls.crt' and 'tls.key'. - properties: - name: - description: name is unique within a namespace to reference - a secret resource. - type: string - namespace: - description: namespace defines the space within which - the secret name must be unique. - type: string - type: object - x-kubernetes-map-type: atomic - required: - - secretRef - type: object - required: - - ca - type: object users: description: List of local users on the switch. items: @@ -677,7 +688,6 @@ spec: type: object type: array required: - - bootstrap - endpoint type: object status: diff --git a/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml b/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml index fea983a2..2787712a 100755 --- a/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml +++ b/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml @@ -35,6 +35,9 @@ spec: - jsonPath: .spec.mtu name: MTU type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date diff --git a/charts/network-operator/templates/network-policy/allow-metrics-traffic.yaml b/charts/network-operator/templates/network-policy/allow-metrics-traffic.yaml index eebe1f14..4d381a54 100755 --- a/charts/network-operator/templates/network-policy/allow-metrics-traffic.yaml +++ b/charts/network-operator/templates/network-policy/allow-metrics-traffic.yaml @@ -19,9 +19,9 @@ spec: ingress: # This allows ingress traffic from any namespace with the label metrics: enabled - from: - - namespaceSelector: - matchLabels: - metrics: enabled # Only from namespaces with this label + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label ports: - port: 8443 protocol: TCP diff --git a/charts/network-operator/templates/rbac/role.yaml b/charts/network-operator/templates/rbac/role.yaml index f5b4f58c..d3ef419a 100755 --- a/charts/network-operator/templates/rbac/role.yaml +++ b/charts/network-operator/templates/rbac/role.yaml @@ -11,7 +11,6 @@ rules: - "" resources: - configmaps - - secrets verbs: - get - list @@ -23,6 +22,15 @@ rules: verbs: - create - patch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - update + - watch - apiGroups: - networking.cloud.sap resources: diff --git a/charts/network-operator/values.yaml b/charts/network-operator/values.yaml index 0a7cac5f..9ff6bb35 100644 --- a/charts/network-operator/values.yaml +++ b/charts/network-operator/values.yaml @@ -12,10 +12,10 @@ controllerManager: resources: limits: cpu: 500m - memory: 512Mi + memory: 128Mi requests: - cpu: 50m - memory: 256Mi + cpu: 10m + memory: 64Mi livenessProbe: initialDelaySeconds: 15 periodSeconds: 20 @@ -74,13 +74,3 @@ certmanager: # [NETWORK POLICIES]: To enable NetworkPolicies set true networkPolicy: enable: false - -owner-info: - helm-chart-url: "https://github.com/ironcore-dev/network-operator/charts/network-operator" - maintainers: - - "felix.kaestner@sap.com" - - "andreas.fritzler@sap.com" - - "sebastian.wagner02@sap.com" - - "enric.pujol@sap.com" - support-group: "network-api" - enabled: true diff --git a/cmd/main.go b/cmd/main.go index 2e78564c..3b26912c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/cache" + + // Set runtime concurrency to match CPU limit imposed by Kubernetes + _ "go.uber.org/automaxprocs" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -170,6 +174,7 @@ func main() { } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Cache: cache.Options{ReaderFailOnMissingInformer: true}, Scheme: scheme, Metrics: metricsServerOptions, WebhookServer: webhookServer, diff --git a/dist/chart/.helmignore b/dist/chart/.helmignore deleted file mode 100644 index 7d92f7fb..00000000 --- a/dist/chart/.helmignore +++ /dev/null @@ -1,25 +0,0 @@ -# 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 deleted file mode 100644 index 5eb1a44f..00000000 --- a/dist/chart/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 6ec3c83c..00000000 --- a/dist/chart/templates/_helpers.tpl +++ /dev/null @@ -1,50 +0,0 @@ -{{- 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 deleted file mode 100644 index c63b8297..00000000 --- a/dist/chart/templates/certmanager/certificate.yaml +++ /dev/null @@ -1,60 +0,0 @@ -{{- 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/crd/networking.cloud.sap_devices.yaml b/dist/chart/templates/crd/networking.cloud.sap_devices.yaml deleted file mode 100755 index d74f4dd9..00000000 --- a/dist/chart/templates/crd/networking.cloud.sap_devices.yaml +++ /dev/null @@ -1,777 +0,0 @@ -{{- if .Values.crd.enable }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - annotations: - {{- if .Values.crd.keep }} - "helm.sh/resource-policy": keep - {{- end }} - controller-gen.kubebuilder.io/version: v0.18.0 - name: devices.networking.cloud.sap -spec: - group: networking.cloud.sap - names: - kind: Device - listKind: DeviceList - plural: devices - singular: device - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.endpoint.address - name: Endpoint - type: string - - jsonPath: .status.phase - name: Phase - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: Device is the Schema for the devices API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of the desired state of the resource. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - properties: - acl: - description: Access Control Lists (ACLs) configuration. - items: - properties: - entries: - description: A list of rules/entries to apply. - items: - properties: - action: - description: The forwarding action of the ACL entry. - enum: - - Permit - - Deny - type: string - destinationAddress: - description: Destination IPv4 address prefix. Use 0.0.0.0/0 - to represent 'any'. - pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$ - type: string - protocol: - default: ip - description: The protocol to match. If not specified, - defaults to "ip" (IPv4). - enum: - - ahp - - eigrp - - esp - - gre - - icmp - - igmp - - ip - - nos - - ospf - - pcp - - pim - - tcp - - udf - - udp - type: string - sequence: - description: The sequence number of the ACL entry. - type: integer - sourceAddress: - description: Source IPv4 address prefix. Use 0.0.0.0/0 - to represent 'any'. - pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$ - type: string - required: - - action - - destinationAddress - - sequence - - sourceAddress - type: object - minItems: 1 - type: array - name: - description: The name of the access control list. - type: string - required: - - entries - - name - type: object - type: array - banner: - description: MOTD banner to display on login. - properties: - configMapRef: - description: Reference to a ConfigMap containing the template - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - inline: - description: Inline template content - type: string - secretRef: - description: Reference to a Secret containing the template - properties: - key: - description: The key of the secret to select from. Must be - a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must be - defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - x-kubernetes-validations: - - message: exactly one of 'inline', 'secretRef', or 'configMapRef' - must be specified - rule: '[has(self.inline), has(self.secretRef), has(self.configMapRef)].filter(x, - x).size() == 1' - bootstrap: - description: |- - Bootstrap is an optional configuration for the device bootstrap process. - It can be used to provide initial configuration templates or scripts that are applied during the device provisioning. - properties: - template: - description: Template defines the multiline string template that - contains the initial configuration for the device. - properties: - configMapRef: - description: Reference to a ConfigMap containing the template - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - inline: - description: Inline template content - type: string - secretRef: - description: Reference to a Secret containing the template - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - x-kubernetes-validations: - - message: exactly one of 'inline', 'secretRef', or 'configMapRef' - must be specified - rule: '[has(self.inline), has(self.secretRef), has(self.configMapRef)].filter(x, - x).size() == 1' - required: - - template - type: object - dns: - description: Top-level configuration for DNS / resolver. - properties: - domain: - description: Default domain name that the switch uses to complete - unqualified hostnames. - format: hostname - type: string - servers: - description: A list of DNS servers to use for address resolution. - items: - properties: - address: - description: The Hostname or IP address of the DNS server. - type: string - networkInstance: - description: The network instance used to communicate with - the DNS server. - type: string - required: - - address - type: object - maxItems: 6 - type: array - srcIf: - description: Source interface for all DNS traffic. - type: string - required: - - domain - type: object - endpoint: - description: Endpoint contains the connection information for the - device. - properties: - address: - description: Address is the management address of the device provided - as . - pattern: ^(\d{1,3}\.){3}\d{1,3}:\d{1,5}$ - type: string - secretRef: - description: |- - SecretRef is name of the authentication secret for the device containing the username and password. - The secret must be of type kubernetes.io/basic-auth and as such contain the following keys: 'username' and 'password'. - properties: - name: - description: name is unique within a namespace to reference - a secret resource. - type: string - namespace: - description: namespace defines the space within which the - secret name must be unique. - type: string - type: object - x-kubernetes-map-type: atomic - tls: - description: Transport credentials for grpc connection to the - switch. - properties: - ca: - description: The CA certificate to verify the server's identity. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - certificate: - description: |- - The client certificate and private key to use for mutual TLS authentication. - Leave empty if mTLS is not desired. - properties: - secretRef: - description: |- - Secret containing the certificate. - The secret must be of type kubernetes.io/tls and as such contain the following keys: 'tls.crt' and 'tls.key'. - properties: - name: - description: name is unique within a namespace to - reference a secret resource. - type: string - namespace: - description: namespace defines the space within which - the secret name must be unique. - type: string - type: object - x-kubernetes-map-type: atomic - required: - - secretRef - type: object - required: - - ca - type: object - required: - - address - type: object - grpc: - description: |- - Configuration for the gRPC server on the device. - Currently, only a single "default" gRPC server is supported. - properties: - certificateId: - description: |- - Name of the certificate that is associated with the gRPC service. - The certificate is provisioned through other interfaces on the device, - such as e.g. the gNOI certificate management service. - type: string - gnmi: - description: |- - Additional gNMI configuration for the gRPC server. - This may not be supported by all devices. - properties: - keepAliveTimeout: - default: 10m - description: |- - Configure the keepalive timeout for inactive or unauthorized connections. - The gRPC agent is expected to periodically send an empty response to the client, on which the client is expected to respond with an empty request. - If the client does not respond within the keepalive timeout, the gRPC agent should close the connection. - The default interval value is 10 minutes. - pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ - type: string - maxConcurrentCall: - default: 8 - description: |- - The maximum number of concurrent gNMI calls that can be made to the gRPC server on the switch for each VRF. - Configure a limit from 1 through 16. The default limit is 8. - maximum: 16 - minimum: 1 - type: integer - minSampleInterval: - default: 10s - description: |- - Configure the minimum sample interval for the gNMI telemetry stream. - Once per stream sample interval, the switch sends the current values for all specified paths. - The default value is 10 seconds. - pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$ - type: string - type: object - networkInstance: - description: Enable the gRPC agent to accept incoming (dial-in) - RPC requests from a given network instance. - type: string - port: - default: 9339 - description: |- - The TCP port on which the gRPC server should listen. - The range of port-id is from 1024 to 65535. - Port 9339 is the default. - format: int32 - maximum: 65535 - minimum: 1024 - type: integer - type: object - logging: - description: Top-level logging configuration for the device. - properties: - facilities: - description: Facilities is a list of log facilities to configure - on the device. - items: - properties: - name: - description: The name of the log facility. - type: string - severity: - description: The severity level of the log messages for - this facility. - enum: - - Debug - - Info - - Notice - - Warning - - Error - - Critical - - Alert - - Emergency - type: string - required: - - name - - severity - type: object - minItems: 1 - type: array - servers: - description: Servers is a list of remote log servers to which - the device will send logs. - items: - properties: - address: - description: IP address or hostname of the remote log server - type: string - networkInstance: - description: The network instance used to reach the log - server. - type: string - port: - default: 514 - description: |- - The destination port number for syslog UDP messages to - the server. The default is 514. - format: int64 - type: integer - severity: - description: The servity level of the log messages sent - to the server. - enum: - - Debug - - Info - - Notice - - Warning - - Error - - Critical - - Alert - - Emergency - type: string - required: - - address - - networkInstance - - severity - type: object - minItems: 1 - type: array - required: - - facilities - - servers - type: object - ntp: - description: Configuration data for system-wide NTP process. - properties: - servers: - description: NTP servers. - items: - properties: - address: - description: Hostname/IP address of the NTP server. - type: string - networkInstance: - description: The network instance used to communicate with - the NTP server. - type: string - prefer: - default: false - description: Indicates whether this server should be preferred - or not. - type: boolean - required: - - address - type: object - minItems: 1 - type: array - srcIf: - description: Source interface for all NTP traffic. - type: string - required: - - servers - - srcIf - type: object - pki: - description: PKI configuration for managing certificates on the device. - properties: - certificates: - description: Certificates is a list of certificates to be managed - by the PKI. - items: - properties: - name: - description: The name of the certificate. - type: string - source: - description: The source of the certificate content. - properties: - secretRef: - description: |- - Secret containing the certificate. - The secret must be of type kubernetes.io/tls and as such contain the following keys: 'tls.crt' and 'tls.key'. - properties: - name: - description: name is unique within a namespace to - reference a secret resource. - type: string - namespace: - description: namespace defines the space within - which the secret name must be unique. - type: string - type: object - x-kubernetes-map-type: atomic - required: - - secretRef - type: object - required: - - name - - source - type: object - minItems: 1 - type: array - required: - - certificates - type: object - snmp: - description: SNMP global configuration. - properties: - communities: - description: SNMP communities for SNMPv1 or SNMPv2c. - items: - properties: - acl: - description: ACL name to filter snmp requests. - type: string - group: - description: Group to which the community belongs. - type: string - name: - description: Name of the community. - type: string - type: object - type: array - contact: - description: The contact information for the SNMP server. - type: string - destinations: - description: SNMP destinations for SNMP traps or informs. - items: - properties: - address: - description: The Hostname or IP address of the SNMP host - to send notifications to. - type: string - networkInstance: - description: The network instance to use to source traffic. - type: string - target: - description: SNMP community or user name. - type: string - type: - default: Traps - description: Type of message to send to host. Default is - traps. - enum: - - Traps - - Inform - type: string - version: - default: v2c - description: SNMP version. Default is v2c. - enum: - - v1 - - v2c - - v3 - type: string - required: - - address - type: object - minItems: 1 - type: array - engineId: - description: The SNMP engine ID for the SNMP server. - type: string - location: - description: The location information for the SNMP server. - type: string - srcIf: - description: Source interface to be used for sending out SNMP - Trap/Inform notifications. - type: string - traps: - description: The list of trap groups to enable. - items: - type: string - type: array - required: - - contact - - destinations - - engineId - - location - - srcIf - type: object - users: - description: List of local users on the switch. - items: - properties: - name: - description: Assigned username for this user. - type: string - password: - description: The user password, supplied as cleartext. - properties: - secretKeyRef: - description: Selects a key of a secret. - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - required: - - secretKeyRef - type: object - role: - description: Role which the user is to be assigned to. - type: string - required: - - name - - password - - role - type: object - type: array - required: - - endpoint - type: object - status: - description: |- - Status of the resource. This is set and updated automatically. - Read-only. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - properties: - conditions: - description: The conditions are a list of status objects that describe - the state of the Device. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - phase: - default: Pending - description: Phase represents the current phase of the Device. - enum: - - Pending - - Provisioning - - Active - - Failed - type: string - required: - - phase - type: object - type: object - served: true - storage: true - subresources: - status: {} -{{- end -}} diff --git a/dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml b/dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml deleted file mode 100755 index 2787712a..00000000 --- a/dist/chart/templates/crd/networking.cloud.sap_interfaces.yaml +++ /dev/null @@ -1,241 +0,0 @@ -{{- if .Values.crd.enable }} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - annotations: - {{- if .Values.crd.keep }} - "helm.sh/resource-policy": keep - {{- end }} - controller-gen.kubebuilder.io/version: v0.18.0 - name: interfaces.networking.cloud.sap -spec: - group: networking.cloud.sap - names: - kind: Interface - listKind: InterfaceList - plural: interfaces - shortNames: - - int - singular: interface - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.name - name: Interface - type: string - - jsonPath: .spec.adminState - name: Admin State - type: string - - jsonPath: .spec.description - name: Description - type: string - - jsonPath: .spec.mtu - name: MTU - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: Interface is the Schema for the interfaces API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - Specification of the desired state of the resource. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - properties: - adminState: - description: AdminState indicates whether the interface is administratively - up or down. - enum: - - Up - - Down - type: string - description: - description: Description provides a human-readable description of - the interface. - maxLength: 255 - type: string - ipv4Addresses: - description: |- - Ipv4Addresses is the list of IPv4 addresses assigned to the interface. - Each address should be given either in CIDR notation (e.g., "10.0.0.1/32") - or as interface reference in the form of "unnumbered:" (e.g., "unnumbered:lo0"). - items: - pattern: ^(?:(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}?|unnumbered:[\w-]+)$ - type: string - type: array - mtu: - description: MTU (Maximum Transmission Unit) specifies the size of - the largest packet that can be sent over the interface. - format: int32 - maximum: 9216 - minimum: 576 - type: integer - name: - description: Name is the name of the interface. - maxLength: 255 - type: string - switchport: - description: |- - Switchport defines the switchport configuration for the interface. - This is only applicable for interfaces that are switchports (e.g., Ethernet interfaces). - properties: - accessVlan: - description: |- - AccessVlan specifies the VLAN ID for access mode switchports. - Only applicable when Mode is set to "Access". - format: int32 - maximum: 4094 - minimum: 1 - type: integer - allowedVlans: - description: |- - AllowedVlans is a list of VLAN IDs that are allowed on the trunk port. - Only applicable when Mode is set to "Trunk". - items: - format: int32 - maximum: 4094 - minimum: 1 - type: integer - minItems: 1 - type: array - mode: - description: Mode defines the switchport mode, such as access - or trunk. - enum: - - Access - - Trunk - type: string - nativeVlan: - description: |- - NativeVlan specifies the native VLAN ID for trunk mode switchports. - Only applicable when Mode is set to "Trunk". - format: int32 - maximum: 4094 - minimum: 1 - type: integer - required: - - mode - type: object - x-kubernetes-validations: - - message: accessVlan must be specified when mode is Access - rule: self.mode != 'Access' || has(self.accessVlan) - - message: nativeVlan must be specified when mode is Trunk - rule: self.mode != 'Trunk' || has(self.nativeVlan) - - message: allowedVlans must be specified when mode is Trunk - rule: self.mode != 'Trunk' || has(self.allowedVlans) - type: - description: Type indicates the type of the interface. - enum: - - Physical - - Loopback - type: string - required: - - adminState - - name - - type - type: object - x-kubernetes-validations: - - message: switchport and ipv4Addresses are mutually exclusive - rule: '!has(self.switchport) || !has(self.ipv4Addresses)' - - message: switchport must not be specified for interfaces of type Loopback - rule: self.type != 'Loopback' || !has(self.switchport) - status: - description: |- - Status of the resource. This is set and updated automatically. - Read-only. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status - properties: - conditions: - description: The conditions are a list of status objects that describe - the state of the Interface. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - served: true - storage: true - subresources: - status: {} -{{- end -}} diff --git a/dist/chart/templates/manager/manager.yaml b/dist/chart/templates/manager/manager.yaml deleted file mode 100644 index 15cf018e..00000000 --- a/dist/chart/templates/manager/manager.yaml +++ /dev/null @@ -1,71 +0,0 @@ -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 deleted file mode 100644 index 9c0db664..00000000 --- a/dist/chart/templates/metrics/metrics-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- 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 deleted file mode 100755 index 4d381a54..00000000 --- a/dist/chart/templates/network-policy/allow-metrics-traffic.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- 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 deleted file mode 100644 index aaa848a1..00000000 --- a/dist/chart/templates/prometheus/monitor.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# 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/device_admin_role.yaml b/dist/chart/templates/rbac/device_admin_role.yaml deleted file mode 100755 index bc1ea098..00000000 --- a/dist/chart/templates/rbac/device_admin_role.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.rbac.enable }} -# This rule is not used by the project network-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants full permissions ('*') over networking.cloud.sap. -# This role is intended for users authorized to modify roles and bindings within the cluster, -# enabling them to delegate specific permissions to other users or groups as needed. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - name: device-admin-role -rules: -- apiGroups: - - networking.cloud.sap - resources: - - devices - verbs: - - '*' -- apiGroups: - - networking.cloud.sap - resources: - - devices/status - verbs: - - get -{{- end -}} diff --git a/dist/chart/templates/rbac/device_editor_role.yaml b/dist/chart/templates/rbac/device_editor_role.yaml deleted file mode 100755 index 5eae2b03..00000000 --- a/dist/chart/templates/rbac/device_editor_role.yaml +++ /dev/null @@ -1,34 +0,0 @@ -{{- if .Values.rbac.enable }} -# This rule is not used by the project network-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants permissions to create, update, and delete resources within the networking.cloud.sap. -# This role is intended for users who need to manage these resources -# but should not control RBAC or manage permissions for others. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - name: device-editor-role -rules: -- apiGroups: - - networking.cloud.sap - resources: - - devices - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - networking.cloud.sap - resources: - - devices/status - verbs: - - get -{{- end -}} diff --git a/dist/chart/templates/rbac/device_viewer_role.yaml b/dist/chart/templates/rbac/device_viewer_role.yaml deleted file mode 100755 index 74f2eb9a..00000000 --- a/dist/chart/templates/rbac/device_viewer_role.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if .Values.rbac.enable }} -# This rule is not used by the project network-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants read-only access to networking.cloud.sap resources. -# This role is intended for users who need visibility into these resources -# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - name: device-viewer-role -rules: -- apiGroups: - - networking.cloud.sap - resources: - - devices - verbs: - - get - - list - - watch -- apiGroups: - - networking.cloud.sap - resources: - - devices/status - verbs: - - get -{{- end -}} diff --git a/dist/chart/templates/rbac/interface_admin_role.yaml b/dist/chart/templates/rbac/interface_admin_role.yaml deleted file mode 100755 index a98f8d8b..00000000 --- a/dist/chart/templates/rbac/interface_admin_role.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.rbac.enable }} -# This rule is not used by the project network-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants full permissions ('*') over networking.cloud.sap. -# This role is intended for users authorized to modify roles and bindings within the cluster, -# enabling them to delegate specific permissions to other users or groups as needed. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - name: interface-admin-role -rules: -- apiGroups: - - networking.cloud.sap - resources: - - interfaces - verbs: - - '*' -- apiGroups: - - networking.cloud.sap - resources: - - interfaces/status - verbs: - - get -{{- end -}} diff --git a/dist/chart/templates/rbac/interface_editor_role.yaml b/dist/chart/templates/rbac/interface_editor_role.yaml deleted file mode 100755 index f0ac3564..00000000 --- a/dist/chart/templates/rbac/interface_editor_role.yaml +++ /dev/null @@ -1,34 +0,0 @@ -{{- if .Values.rbac.enable }} -# This rule is not used by the project network-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants permissions to create, update, and delete resources within the networking.cloud.sap. -# This role is intended for users who need to manage these resources -# but should not control RBAC or manage permissions for others. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - name: interface-editor-role -rules: -- apiGroups: - - networking.cloud.sap - resources: - - interfaces - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - networking.cloud.sap - resources: - - interfaces/status - verbs: - - get -{{- end -}} diff --git a/dist/chart/templates/rbac/interface_viewer_role.yaml b/dist/chart/templates/rbac/interface_viewer_role.yaml deleted file mode 100755 index 0f2b1c41..00000000 --- a/dist/chart/templates/rbac/interface_viewer_role.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if .Values.rbac.enable }} -# This rule is not used by the project network-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants read-only access to networking.cloud.sap resources. -# This role is intended for users who need visibility into these resources -# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "chart.labels" . | nindent 4 }} - name: interface-viewer-role -rules: -- apiGroups: - - networking.cloud.sap - resources: - - interfaces - verbs: - - get - - list - - watch -- apiGroups: - - networking.cloud.sap - resources: - - interfaces/status - verbs: - - get -{{- end -}} diff --git a/dist/chart/templates/rbac/leader_election_role.yaml b/dist/chart/templates/rbac/leader_election_role.yaml deleted file mode 100755 index 9b5bccc2..00000000 --- a/dist/chart/templates/rbac/leader_election_role.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{- 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 deleted file mode 100755 index 12254217..00000000 --- a/dist/chart/templates/rbac/leader_election_role_binding.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- 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 deleted file mode 100755 index 55191de8..00000000 --- a/dist/chart/templates/rbac/metrics_auth_role.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- 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 deleted file mode 100755 index 04094347..00000000 --- a/dist/chart/templates/rbac/metrics_auth_role_binding.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- 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 deleted file mode 100755 index 91d0f252..00000000 --- a/dist/chart/templates/rbac/metrics_reader_role.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- 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 deleted file mode 100755 index d3ef419a..00000000 --- a/dist/chart/templates/rbac/role.yaml +++ /dev/null @@ -1,63 +0,0 @@ -{{- 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: - - configmaps - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - list - - update - - watch -- apiGroups: - - networking.cloud.sap - resources: - - devices - - interfaces - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - networking.cloud.sap - resources: - - devices/finalizers - - interfaces/finalizers - verbs: - - update -- apiGroups: - - networking.cloud.sap - resources: - - devices/status - - interfaces/status - verbs: - - get - - patch - - update -{{- end -}} diff --git a/dist/chart/templates/rbac/role_binding.yaml b/dist/chart/templates/rbac/role_binding.yaml deleted file mode 100755 index 5787fe37..00000000 --- a/dist/chart/templates/rbac/role_binding.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- 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 deleted file mode 100755 index 93e0a323..00000000 --- a/dist/chart/templates/rbac/service_account.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- 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 deleted file mode 100644 index 9ff6bb35..00000000 --- a/dist/chart/values.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# [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/go.mod b/go.mod index 34bb2b71..1e0e8152 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/openconfig/goyang v1.6.2 github.com/openconfig/ygnmi v0.13.1-0.20250924235719-646562b5d0c3 github.com/openconfig/ygot v0.32.0 + go.uber.org/automaxprocs v1.6.0 go.uber.org/zap v1.27.0 google.golang.org/grpc v1.71.0 google.golang.org/protobuf v1.36.5 diff --git a/go.sum b/go.sum index 3fb00d76..67af6884 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= @@ -174,6 +176,8 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= From 0be428de558f562a9d94b026dd82e94bd72f0fb2 Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Wed, 27 Aug 2025 13:54:28 +0200 Subject: [PATCH 3/5] Add version subcommand --- .github/workflows/publish-docker.yml | 8 ++++++++ .golangci.yml | 3 +++ .license-scan-overrides.jsonl | 11 ----------- .license-scan-rules.json | 14 -------------- Dockerfile | 19 +++++++++++++++++-- Makefile | 14 ++++++++++++-- api/v1alpha1/doc.go | 3 ++- api/v1alpha1/groupversion_info.go | 3 --- cmd/main.go | 15 +++++++++++++-- 9 files changed, 55 insertions(+), 35 deletions(-) delete mode 100644 .license-scan-overrides.jsonl delete mode 100644 .license-scan-rules.json diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 566d0eaf..d4da8609 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -53,6 +53,10 @@ jobs: flavor: | latest=${{ github.ref == 'refs/heads/main' }} + - name: Set short git commit SHA + id: vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: @@ -82,3 +86,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} target: ${{ matrix.image.target }} + build-args: | + VERSION=${{ steps.meta.outputs.version }} + GIT_COMMIT=${{ steps.vars.sha_short }} + BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') diff --git a/.golangci.yml b/.golangci.yml index 6c99fa10..a7c2eb6d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,6 +39,9 @@ linters: - usetesting - whitespace settings: + gomoddirectives: + toolchain-forbidden: true + go-version-pattern: 1\.\d+(\.0)?$ revive: rules: - name: comment-spacings diff --git a/.license-scan-overrides.jsonl b/.license-scan-overrides.jsonl deleted file mode 100644 index 0a8feb2e..00000000 --- a/.license-scan-overrides.jsonl +++ /dev/null @@ -1,11 +0,0 @@ -{"name": "github.com/chzyer/logex", "licenceType": "MIT"} -{"name": "github.com/hashicorp/vault/api/auth/approle", "licenceType": "MPL-2.0"} -{"name": "github.com/jpillora/longestcommon", "licenceType": "MIT"} -{"name": "github.com/logrusorgru/aurora", "licenceType": "Unlicense"} -{"name": "github.com/mattn/go-localereader", "licenceType": "MIT"} -{"name": "github.com/miekg/dns", "licenceType": "BSD-3-Clause"} -{"name": "github.com/pashagolub/pgxmock/v4", "licenceType": "BSD-3-Clause"} -{"name": "github.com/spdx/tools-golang", "licenceTextOverrideFile": "vendor/github.com/spdx/tools-golang/LICENSE.code"} -{"name": "github.com/xeipuuv/gojsonpointer", "licenceType": "Apache-2.0"} -{"name": "github.com/xeipuuv/gojsonreference", "licenceType": "Apache-2.0"} -{"name": "github.com/xeipuuv/gojsonschema", "licenceType": "Apache-2.0"} diff --git a/.license-scan-rules.json b/.license-scan-rules.json deleted file mode 100644 index 909cc0fa..00000000 --- a/.license-scan-rules.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "allowlist": [ - "Apache-2.0", - "BSD-2-Clause", - "BSD-2-Clause-FreeBSD", - "BSD-3-Clause", - "EPL-2.0", - "ISC", - "MIT", - "MPL-2.0", - "Unlicense", - "Zlib" - ] -} diff --git a/Dockerfile b/Dockerfile index b1645e3b..b067d8f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,10 @@ ARG BININFO_VERSION ARG TARGETOS ARG TARGETARCH +ARG BUILD_DATE +ARG GIT_COMMIT +ARG VERSION + WORKDIR /workspace # Copy the Go Modules manifests COPY go.mod go.mod @@ -33,12 +37,23 @@ COPY internal/ internal/ # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. 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 -ldflags="-s -w" -a -o manager cmd/main.go + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -ldflags="-s -w -X 'main.version=${VERSION}' -X 'main.gitCommit=${GIT_COMMIT}' -X 'main.buildDate=${BUILD_DATE}'" -a -o manager cmd/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 -LABEL source_repository="https://github.com/ironcore-dev/metal-operator" + +ARG BUILD_DATE +ARG GIT_COMMIT +ARG VERSION + +LABEL source_repository="https://github.com/ironcore-dev/metal-operator" \ + org.opencontainers.image.url="https://github.com/ironcore-dev/network-operator" \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.revision=${GIT_COMMIT} \ + org.opencontainers.image.version=${VERSION} \ + org.opencontainers.image.licenses="Apache-2.0" + WORKDIR / COPY --from=builder /workspace/manager . USER 65532:65532 diff --git a/Makefile b/Makefile index 9e1ba694..38af5679 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,12 @@ endif GOARCH := $(shell go env GOARCH) GOOS := $(shell go env GOOS) +VERSION ?= dev +GIT_COMMIT ?= $(shell git rev-parse --short HEAD) +BUILD_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') + +LDFLAGS = -ldflags="-X 'main.version=$(VERSION)' -X 'main.gitCommit=$(GIT_COMMIT)' -X 'main.buildDate=$(BUILD_DATE)'" + # CONTAINER_TOOL defines the container tool to be used for building images. # 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 @@ -127,7 +133,7 @@ docs: gen-crd-api-reference-docs ## Run go generate to generate API reference do .PHONY: build build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go + go build $(LDFLAGS) -o bin/manager cmd/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. @@ -143,7 +149,11 @@ helm: manifests kubebuilder # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build docker-build: ## Build docker image with the manager. - $(CONTAINER_TOOL) build -t ${IMG} . + $(CONTAINER_TOOL) build \ + --build-arg=BUILD_DATE=$(BUILD_DATE) \ + --build-arg=GIT_COMMIT=$(GIT_COMMIT) \ + --build-arg=VERSION=$(VERSION) \ + -t ${IMG} . .PHONY: docker-push docker-push: ## Push docker image with the manager. diff --git a/api/v1alpha1/doc.go b/api/v1alpha1/doc.go index f093f72f..e3ae75c0 100644 --- a/api/v1alpha1/doc.go +++ b/api/v1alpha1/doc.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Package v1alpha1 contains API Schema definitions for the networking.cloud.sap API group -// +groupName=networking.cloud.sap +// +kubebuilder:validation:Required // +kubebuilder:object:generate=true +// +groupName=networking.cloud.sap package v1alpha1 diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index e94846d0..baedbc42 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -2,9 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // Package v1alpha1 contains API Schema definitions for the networking.cloud.sap v1alpha1 API group. -// +kubebuilder:validation:Required -// +kubebuilder:object:generate=true -// +groupName=networking.cloud.sap package v1alpha1 import ( diff --git a/cmd/main.go b/cmd/main.go index 3b26912c..72cf4d26 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "flag" "fmt" + "log" "os" "path/filepath" "strings" @@ -20,8 +21,6 @@ import ( // Set runtime concurrency to match CPU limit imposed by Kubernetes _ "go.uber.org/automaxprocs" - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/certwatcher" @@ -44,6 +43,10 @@ import ( var ( scheme = runtime.NewScheme() setupLog = ctrl.Log.WithName("setup") + + version = "dev" + gitCommit = "none" + buildDate = "unknown" ) func init() { @@ -53,6 +56,14 @@ func init() { } func main() { + if len(os.Args) > 1 && os.Args[1] == "version" { + log.SetFlags(0) + log.Printf("Version: %s", version) + log.Printf("Git Commit: %s", gitCommit) + log.Printf("Build Date: %s", buildDate) + os.Exit(0) + } + var metricsAddr string var metricsCertPath, metricsCertName, metricsCertKey string var webhookCertPath, webhookCertName, webhookCertKey string From 71d8f25eebcac937af2c58f0496340e8bff2d1ba Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Tue, 9 Sep 2025 17:23:29 +0200 Subject: [PATCH 4/5] Fix linting issues --- .github/workflows/publish-docker.yml | 8 ++--- .golangci.yml | 1 + test/utils/util.go | 54 ---------------------------- 3 files changed, 3 insertions(+), 60 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index d4da8609..7d0e5e02 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -53,10 +53,6 @@ jobs: flavor: | latest=${{ github.ref == 'refs/heads/main' }} - - name: Set short git commit SHA - id: vars - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: @@ -88,5 +84,5 @@ jobs: target: ${{ matrix.image.target }} build-args: | VERSION=${{ steps.meta.outputs.version }} - GIT_COMMIT=${{ steps.vars.sha_short }} - BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') + GIT_COMMIT=${{ github.sha }} + BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" diff --git a/.golangci.yml b/.golangci.yml index a7c2eb6d..da7ecc31 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -79,6 +79,7 @@ linters: path: internal/provider/cisco/nxos/vrf - linters: - lll + - goconst path: internal/provider/cisco/nxos - linters: - lll diff --git a/test/utils/util.go b/test/utils/util.go index 7e6fe073..2cf31a3d 100644 --- a/test/utils/util.go +++ b/test/utils/util.go @@ -4,8 +4,6 @@ package utils import ( - "bufio" - "bytes" "fmt" "os" "os/exec" @@ -200,55 +198,3 @@ func GetProjectDir() (string, error) { wd = strings.ReplaceAll(wd, "/test/e2e", "") return wd, nil } - -// UncommentCode searches for target in the file and remove the comment prefix -// of the target content. The target content may span multiple lines. -func UncommentCode(filename, target, prefix string) error { - // false positive - // nolint:gosec - content, err := os.ReadFile(filename) - if err != nil { - return fmt.Errorf("failed to read file %q: %w", filename, err) - } - strContent := string(content) - - idx := strings.Index(strContent, target) - if idx < 0 { - return fmt.Errorf("unable to find the code %q to be uncomment", target) - } - - out := new(bytes.Buffer) - _, err = out.Write(content[:idx]) - if err != nil { - return fmt.Errorf("failed to write to output: %w", err) - } - - scanner := bufio.NewScanner(bytes.NewBufferString(target)) - if !scanner.Scan() { - return nil - } - for { - if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil { - return fmt.Errorf("failed to write to output: %w", err) - } - // Avoid writing a newline in case the previous line was the last in target. - if !scanner.Scan() { - break - } - if _, err = out.WriteString("\n"); err != nil { - return fmt.Errorf("failed to write to output: %w", err) - } - } - - if _, err = out.Write(content[idx+len(target):]); err != nil { - return fmt.Errorf("failed to write to output: %w", err) - } - - // false positive - // nolint:gosec - if err = os.WriteFile(filename, out.Bytes(), 0o644); err != nil { - return fmt.Errorf("failed to write file %q: %w", filename, err) - } - - return nil -} From 559abfa0fdd4bdc87b684fb1bf611a211fb33619 Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Tue, 7 Oct 2025 14:38:36 +0200 Subject: [PATCH 5/5] Regenerate code --- .github/workflows/publish-docker.yml | 6 +- .golangci.yml | 9 ++ Dockerfile | 8 +- Makefile | 2 +- .../crd/networking.cloud.sap_interfaces.yaml | 59 +++++++ .../networking.cloud.sap_interfaces.yaml | 2 +- docs/api-reference/api.md | 146 +++++++++++++++++- internal/provider/cisco/nxos/gnmiext/mock.go | 3 +- .../cisco/nxos/iface/loopback_test.go | 1 - .../cisco/nxos/iface/portchannel_test.go | 1 + .../provider/cisco/nxos/isis/interface.go | 5 +- .../cisco/nxos/isis/interface_test.go | 1 + 12 files changed, 227 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 7d0e5e02..7aea3479 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -72,6 +72,10 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Get and Save Date + id: date_step + run: echo "current_datetime=$(date +'%Y-%m-%d %H:%M:%S')" >> "$GITHUB_OUTPUT" + - name: Build and push timeout-minutes: 40 uses: docker/build-push-action@v6 @@ -85,4 +89,4 @@ jobs: build-args: | VERSION=${{ steps.meta.outputs.version }} GIT_COMMIT=${{ github.sha }} - BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" + BUILD_DATE="${{ steps.date_step.outputs.current_datetime }}" diff --git a/.golangci.yml b/.golangci.yml index da7ecc31..6f458646 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -77,10 +77,19 @@ linters: - gocyclo - dupl path: internal/provider/cisco/nxos/vrf + - linters: + - whitespace + path: internal/provider/cisco/nxos/nve - linters: - lll - goconst path: internal/provider/cisco/nxos + - linters: + - goimports + - gosec + - perfsprint + - errcheck + path: internal/provider/cisco/nxos/isis - linters: - lll - perfsprint diff --git a/Dockerfile b/Dockerfile index b067d8f7..334ac344 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,7 @@ # 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 +FROM golang:1.25 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/Makefile b/Makefile index 38af5679..bb725ceb 100644 --- a/Makefile +++ b/Makefile @@ -242,7 +242,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.18.0 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.4 +GOLANGCI_LINT_VERSION ?= v2.5 GOIMPORTS_VERSION ?= v0.31.0 GEN_CRD_API_REFERENCE_DOCS_VERSION ?= v0.3.0 KUBEBUILDER_VERSION ?= v4.5.1 diff --git a/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml b/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml index 2787712a..8386e211 100755 --- a/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml +++ b/charts/network-operator/templates/crd/networking.cloud.sap_interfaces.yaml @@ -35,6 +35,9 @@ spec: - jsonPath: .spec.mtu name: MTU type: string + - jsonPath: .spec.deviceName + name: Device + type: string - jsonPath: .status.conditions[?(@.type=="Ready")].status name: Ready type: string @@ -80,6 +83,25 @@ spec: the interface. maxLength: 255 type: string + deviceRef: + description: |- + DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace. + Immutable. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + maxLength: 63 + minLength: 1 + type: string + required: + - name + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: DeviceRef is immutable + rule: self == oldSelf ipv4Addresses: description: |- Ipv4Addresses is the list of IPv4 addresses assigned to the interface. @@ -100,6 +122,40 @@ spec: description: Name is the name of the interface. maxLength: 255 type: string + providerConfigRef: + description: |- + ProviderConfigRef is a reference to a resource holding the provider-specific configuration of this interface. + This reference is used to link the Interface to its provider-specific configuration. + properties: + apiVersion: + description: APIVersion is the api group version of the resource + being referenced. + maxLength: 253 + minLength: 1 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/)?([a-z0-9]([-a-z0-9]*[a-z0-9])?)$ + type: string + kind: + description: |- + Kind of the resource being referenced. + Kind must consist of alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name of the resource being referenced. + Name must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - apiVersion + - kind + - name + type: object + x-kubernetes-map-type: atomic switchport: description: |- Switchport defines the switchport configuration for the interface. @@ -157,6 +213,7 @@ spec: type: string required: - adminState + - deviceRef - name - type type: object @@ -233,6 +290,8 @@ spec: - type x-kubernetes-list-type: map type: object + required: + - spec type: object served: true storage: true diff --git a/config/crd/bases/networking.cloud.sap_interfaces.yaml b/config/crd/bases/networking.cloud.sap_interfaces.yaml index 5342264b..e4e9efb1 100644 --- a/config/crd/bases/networking.cloud.sap_interfaces.yaml +++ b/config/crd/bases/networking.cloud.sap_interfaces.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: interfaces.networking.cloud.sap spec: group: networking.cloud.sap diff --git a/docs/api-reference/api.md b/docs/api-reference/api.md index 364b5d2d..80fb4c90 100644 --- a/docs/api-reference/api.md +++ b/docs/api-reference/api.md @@ -1056,6 +1056,35 @@ More info: +LocalObjectReference + + + + +

    DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace. +Immutable.

    + + + + +providerConfigRef
    + + +TypedLocalObjectReference + + + + +(Optional) +

    ProviderConfigRef is a reference to a resource holding the provider-specific configuration of this interface. +This reference is used to link the Interface to its provider-specific configuration.

    + + + + name
    string @@ -1149,7 +1178,7 @@ or as interface reference in the form of “unnumbered:&rd -status
    +status,omitempty,omitzero
    InterfaceStatus @@ -1157,6 +1186,7 @@ InterfaceStatus +(Optional)

    Status of the resource. This is set and updated automatically. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

    @@ -1182,6 +1212,35 @@ More info: +LocalObjectReference + +
    + + +

    DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace. +Immutable.

    + + + + +providerConfigRef
    + + +TypedLocalObjectReference + + + + +(Optional) +

    ProviderConfigRef is a reference to a resource holding the provider-specific configuration of this interface. +This reference is used to link the Interface to its provider-specific configuration.

    + + + + name
    string @@ -1327,6 +1386,37 @@ or as interface reference in the form of “unnumbered:&rd +

    LocalObjectReference +

    +

    +(Appears on:InterfaceSpec) +

    +
    +

    LocalObjectReference contains enough information to locate a +referenced object inside the same namespace.

    +
    + + + + + + + + + + + + + +
    FieldDescription
    +name
    + +string + +
    +

    Name of the referent. +More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names

    +

    LogFacility

    @@ -2111,6 +2201,60 @@ Kubernetes core/v1.ConfigMapKeySelector +

    TypedLocalObjectReference +

    +

    +(Appears on:InterfaceSpec) +

    +
    +

    TypedLocalObjectReference contains enough information to locate a +typed referenced object inside the same namespace.

    +
    + + + + + + + + + + + + + + + + + + + + + +
    FieldDescription
    +kind
    + +string + +
    +

    Kind of the resource being referenced. +Kind must consist of alphanumeric characters or ‘-’, start with an alphabetic character, and end with an alphanumeric character.

    +
    +name
    + +string + +
    +

    Name of the resource being referenced. +Name must consist of lower case alphanumeric characters, ‘-’ or ‘.’, and must start and end with an alphanumeric character.

    +
    +apiVersion
    + +string + +
    +

    APIVersion is the api group version of the resource being referenced.

    +

    User

    diff --git a/internal/provider/cisco/nxos/gnmiext/mock.go b/internal/provider/cisco/nxos/gnmiext/mock.go index ca1bb44b..8d6f9609 100644 --- a/internal/provider/cisco/nxos/gnmiext/mock.go +++ b/internal/provider/cisco/nxos/gnmiext/mock.go @@ -5,10 +5,11 @@ package gnmiext import ( "context" + "sync" + gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ygot/ygot" "google.golang.org/grpc" - "sync" ) // Ensure, that GNMIClientMock does implement GNMIClient. diff --git a/internal/provider/cisco/nxos/iface/loopback_test.go b/internal/provider/cisco/nxos/iface/loopback_test.go index 569b5995..10e57409 100644 --- a/internal/provider/cisco/nxos/iface/loopback_test.go +++ b/internal/provider/cisco/nxos/iface/loopback_test.go @@ -103,7 +103,6 @@ func Test_Loopback_ToYGOT_BaseConfig(t *testing.T) { } func Test_Loopback_ToYGOT_WithL3Config(t *testing.T) { - testAddressingL3Cfg, err := NewL3Config( WithNumberedAddressingIPv4([]string{"10.0.0.1/24"}), ) diff --git a/internal/provider/cisco/nxos/iface/portchannel_test.go b/internal/provider/cisco/nxos/iface/portchannel_test.go index 81a55dc2..d79004fc 100644 --- a/internal/provider/cisco/nxos/iface/portchannel_test.go +++ b/internal/provider/cisco/nxos/iface/portchannel_test.go @@ -276,6 +276,7 @@ func Test_PortChannel_ToYGOT(t *testing.T) { }) } } + func Test_PortChannel_Reset(t *testing.T) { tests := []struct { name string diff --git a/internal/provider/cisco/nxos/isis/interface.go b/internal/provider/cisco/nxos/isis/interface.go index e1b67cd3..db881072 100644 --- a/internal/provider/cisco/nxos/isis/interface.go +++ b/internal/provider/cisco/nxos/isis/interface.go @@ -16,9 +16,7 @@ import ( "github.com/ironcore-dev/network-operator/internal/provider/cisco/nxos/iface" ) -var ( - ErrUnsupported error = errors.New("isis: unsupported interface type for isis") -) +var ErrUnsupported error = errors.New("isis: unsupported interface type for isis") type Interface struct { name string // interface name, e.g., Ethernet1/1 @@ -76,6 +74,7 @@ func WithPointToPoint() IfOption { return nil } } + func WithBFD() IfOption { return func(i *Interface) error { i.bfd = true diff --git a/internal/provider/cisco/nxos/isis/interface_test.go b/internal/provider/cisco/nxos/isis/interface_test.go index f67b1c9d..87aa0b55 100644 --- a/internal/provider/cisco/nxos/isis/interface_test.go +++ b/internal/provider/cisco/nxos/isis/interface_test.go @@ -53,6 +53,7 @@ func Test_Interface_NewInterface(t *testing.T) { }) } } + func Test_Interface_toYGOT(t *testing.T) { tests := []struct { name string