diff --git a/.editorconfig b/.editorconfig index 46ace6840..b4c60bea6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,14 +1,15 @@ -# SPDX-FileCopyrightText: SAP SE or an SAP affiliate company +# SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors # SPDX-License-Identifier: Apache-2.0 root = true [*] -insert_final_newline = true charset = utf-8 -trim_trailing_whitespace = true -indent_style = space +end_of_line = lf indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true [{Makefile,go.mod,go.sum,*.go}] indent_style = tab diff --git a/.github/workflows/check-codegen.yml b/.github/workflows/check-codegen.yml index b7a1b1741..3bcb60f74 100644 --- a/.github/workflows/check-codegen.yml +++ b/.github/workflows/check-codegen.yml @@ -22,16 +22,18 @@ jobs: go-version-file: 'go.mod' - name: Run make generate run: make generate + - name: Run make manifests + run: make manifests - name: Run make docs run: make docs - - name: Run make charts - run: make charts + - 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 charts && make fmt'." + echo "Detected uncommitted changes after build. Consider running 'make generate && make manifests && make docs && make helm && make fmt'." echo "See status below:" git diff exit 1 diff --git a/.github/workflows/kustomize-validation.yml b/.github/workflows/kustomize-validation.yml index 4b0e38d7e..8bf7ec721 100644 --- a/.github/workflows/kustomize-validation.yml +++ b/.github/workflows/kustomize-validation.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v6 - name: Install Kustomize run: | - make install-kustomize + make kustomize - name: Validate Kustomize run: | ./hack/validate-kustomize.sh diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fb164b73d..5bb58dbd5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -60,7 +60,11 @@ jobs: - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' - - name: Dependency Licenses Review - run: make check-dependency-licenses - - name: Check if source code files have license header - run: make check-addlicense + - name: Install addlicense + run: go install github.com/google/addlicense@latest + - name: Check License Headers + run: addlicense -check -ignore '**/*.yml' -ignore '**/*.yaml' . + - name: Install go-licenses + run: go install github.com/google/go-licenses@latest + - name: Check Dependency Licenses + run: go-licenses check --include_tests --disallowed_types=restricted,forbidden,unknown ./... diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 8ff91ac67..0f66558bd 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -88,9 +88,9 @@ jobs: with: context: . build-args: | - BININFO_VERSION=${{ steps.build_args.outputs.version }} - BININFO_COMMIT_HASH=${{ steps.build_args.outputs.commit }} - BININFO_BUILD_DATE=${{ steps.build_args.outputs.date }} + VERSION=${{ steps.build_args.outputs.version }} + GIT_COMMIT=${{ steps.build_args.outputs.commit }} + BUILD_DATE=${{ steps.build_args.outputs.date }} platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec4018824..b7c0c8bce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,12 +28,12 @@ jobs: - name: Generate release info run: | go install github.com/sapcc/go-bits/tools/release-info@latest - mkdir -p build - release-info CHANGELOG.md "$(git describe --tags --abbrev=0)" > build/release-info + mkdir -p dist + release-info CHANGELOG.md "$(git describe --tags --abbrev=0)" > dist/release-info - name: Run GoReleaser uses: goreleaser/goreleaser-action@v7 with: - args: release --clean --release-notes=./build/release-info + args: release --clean --release-notes=./dist/release-info version: latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2c91b1c60..34d0b16f8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -33,8 +33,8 @@ jobs: go-version-file: 'go.mod' - name: Run prepare make target run: make generate - - name: Build all binaries - run: make build-all + - name: Build manager binary + run: make build test: name: Test needs: build @@ -44,15 +44,15 @@ jobs: - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' - - name: Run prepare make target + - name: Run generate make target run: make generate - name: Run tests and generate coverage report - run: make build/cover.out + run: make coverage - name: Archive code coverage results uses: actions/upload-artifact@v7 with: name: code-coverage - path: build/cover.out + path: cover.out code-coverage: name: Code Coverage Report needs: test diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 6076f5450..689180f85 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,5 +1,5 @@ # yaml-language-server: $schema=https://goreleaser.com/static/schema.json -# SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company +# SPDX-FileCopyrightText: 2020 SAP SE or an SAP affiliate company and IronCore contributors # SPDX-License-Identifier: Apache-2.0 version: 2 @@ -19,7 +19,6 @@ builds: - CGO_ENABLED=0 goos: - linux - - windows - darwin goarch: - amd64 @@ -29,10 +28,9 @@ builds: goarch: arm64 ldflags: - -s -w - - -X github.com/sapcc/go-api-declarations/bininfo.binName=network-operator - - -X github.com/sapcc/go-api-declarations/bininfo.version={{ .Version }} - - -X github.com/sapcc/go-api-declarations/bininfo.commit={{ .FullCommit }} - - -X github.com/sapcc/go-api-declarations/bininfo.buildDate={{ .CommitDate }} # use CommitDate instead of Date for reproducibility + - -X main.version={{ .Version }} + - -X main.gitCommit={{ .FullCommit }} + - -X main.buildDate={{ .CommitDate }} # use CommitDate instead of Date for reproducibility main: ./cmd # Set the modified timestamp on the output binary to ensure that builds are reproducible. mod_timestamp: "{{ .CommitTimestamp }}" diff --git a/.license-scan-overrides.jsonl b/.license-scan-overrides.jsonl deleted file mode 100644 index faa62bcb5..000000000 --- a/.license-scan-overrides.jsonl +++ /dev/null @@ -1,13 +0,0 @@ -{"name": "github.com/chzyer/logex", "licenceType": "MIT"} -{"name": "github.com/grpc-ecosystem/go-grpc-middleware/v2", "licenceType": "Apache-2.0"} -{"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/pashagolub/pgxmock/v5", "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 909cc0fa9..000000000 --- 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/.typos.toml b/.typos.toml index b9215bcbd..b067bffbe 100644 --- a/.typos.toml +++ b/.typos.toml @@ -1,5 +1,6 @@ -# SPDX-FileCopyrightText: 2026 SAP SE +# SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors # SPDX-License-Identifier: Apache-2.0 + [default] extend-ignore-re = [ "Cisco-IOS-XR.*" diff --git a/Dockerfile b/Dockerfile index 8619d4333..fb2772adb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,9 @@ FROM --platform=$BUILDPLATFORM golang:1.26-alpine3.22 AS builder -ARG BININFO_BUILD_DATE -ARG BININFO_COMMIT_HASH -ARG BININFO_VERSION +ARG VERSION +ARG GIT_COMMIT +ARG BUILD_DATE ARG TARGETOS ARG TARGETARCH @@ -21,19 +21,19 @@ RUN --mount=type=cache,target=/go/pkg/mod \ RUN --mount=type=bind,target=. \ --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ - GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOTOOLCHAIN=local CGO_ENABLED=0 go build -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}" -o /usr/bin/network-operator ./cmd + GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOTOOLCHAIN=local CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT} -X main.buildDate=${BUILD_DATE}" -o /usr/bin/network-operator ./cmd FROM gcr.io/distroless/static:nonroot -ARG BININFO_BUILD_DATE -ARG BININFO_COMMIT_HASH -ARG BININFO_VERSION +ARG VERSION +ARG GIT_COMMIT +ARG BUILD_DATE 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} \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.revision=${GIT_COMMIT} \ + org.opencontainers.image.version=${VERSION} \ org.opencontainers.image.licenses="Apache-2.0" COPY --from=builder /usr/bin/network-operator /manager diff --git a/Makefile b/Makefile index 330328a6d..591d5e995 100644 --- a/Makefile +++ b/Makefile @@ -1,405 +1,416 @@ -################################################################################ -# 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 - -# macOS ships with make 3.81 from 2006, which does not support all the features that we want (e.g. --warn-undefined-variables) -ifeq ($(MAKE_VERSION),3.81) - ifeq (,$(shell which gmake 2>/dev/null)) - $(error We do not support this "make" version ($(MAKE_VERSION)) which is two decades old. Please install a newer version, e.g. using "brew install make") - else - $(error We do not support this "make" version ($(MAKE_VERSION)) which is two decades old. You have a newer GNU make installed, so please run "gmake" instead) - endif -endif +# Image URL to use all building/pushing image targets +IMG ?= controller:latest -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 +# 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 -default: build-all +GOOS := $(shell go env GOOS) +GOARCH := $(shell go env GOARCH) -# Image to use all building/pushing image targets -IMG ?= controller:latest +VERSION ?= $(shell git describe --tags --always --abbrev=7) +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. -# 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. However, +# you might want to replace it to use other tools. (i.e. podman, nerdctl) CONTAINER_TOOL ?= docker -# KIND_CLUSTER defines the name of the Kind cluster to be used for the tilt setup. -KIND_CLUSTER ?= network - -LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) - -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-kubebuilder: FORCE - @set -eou pipefail; if ! hash kubebuilder 2>/dev/null; then printf "\e[1;36m>> Installing kubebuilder...\e[0m\n"; 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 kubebuilder"; exit 2; fi; BIN=$$(go env GOBIN); if [[ -z $$BIN ]]; then BIN=$$(go env GOPATH)/bin; fi; $$GET "$$BIN/kubebuilder" "https://go.kubebuilder.io/dl/latest/$$(go env GOOS)/$$(go env GOARCH)"; chmod +x "$$BIN/kubebuilder"; 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 - -install-crd-ref-docs: FORCE - @if ! hash crd-ref-docs 2>/dev/null; then printf "\e[1;36m>> Installing crd-ref-docs...\e[0m\n"; go install github.com/elastic/crd-ref-docs@latest; fi - -bin/golangci-lint-custom: .custom-gcl.yaml - @hash golangci-lint 2>/dev/null || (printf "\e[1;31m>> golangci-lint not found, please install it first\e[0m\n"; exit 1) - golangci-lint custom --destination $(LOCALBIN) --name golangci-lint-custom - -lint: FORCE bin/golangci-lint-custom ## Run golangci-lint linter - @printf "\e[1;36m>> golangci-lint\e[0m\n" - @bin/golangci-lint-custom config verify - @bin/golangci-lint-custom run - -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') +# 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 + +##@ 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) crd rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +YEAR ?= $(shell date +%Y) + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt",year="$(YEAR)" paths="./..." + $(CONTROLLER_GEN) applyconfiguration:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: goimports gofumpt ## Run goimports and gofumpt against code. + @$(GOIMPORTS) -w -local github.com/ironcore-dev/network-operator $(shell git ls-files '*.go' | grep -E -v 'internal/provider/openconfig|zz_generated.deepcopy.go') + @$(GOFUMPT) -l -w $(shell git ls-files '*.go' | grep -E -v 'internal/provider/openconfig|zz_generated.deepcopy.go') + +.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 + +.PHONY: coverage +coverage: test ## Run tests and generate coverage report. + go tool cover -html=cover.out -o cover.html + +# TODO(felix-kaestner): 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. +KIND_CLUSTER ?= network-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: kind ## Set up a Kind cluster for e2e tests if it does not exist + @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: test-lab +test-lab: ## Run lab tests against a real network device. + go test ./test/lab/ -v + +.PHONY: lint +lint: golangci-lint-custom ## Run golangci-lint linter + $(GOLANGCI_LINT_CUSTOM) run + +.PHONY: lint-fix +lint-fix: golangci-lint-custom ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT_CUSTOM) run --fix + +.PHONY: lint-config +lint-config: golangci-lint-custom ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT_CUSTOM) config verify + +.PHONY: lint-sh +lint-sh: shellcheck ## Run shellcheck on shell scripts. + find . -name '*.sh' -not -path './bin/*' -not -path '*/node_modules/*' -exec $(SHELLCHECK) {} + + +.PHONY: spellcheck +spellcheck: typos ## Run typos spell checker. + $(TYPOS) + +.PHONY: add-license +add-license: addlicense ## Add license headers to all go files. + $(ADDLICENSE) -ignore '**/*.yml' -ignore '**/*.yaml' -f hack/license-header.txt . + +.PHONY: check-license +check-license: addlicense ## Check that every file has a license header present. + $(ADDLICENSE) -ignore '**/*.yml' -ignore '**/*.yaml' -check -c 'IronCore contributors' . + +.PHONY: check-dependency-license +check-dependency-license: go-licenses ## Check that dependencies don't have non-FOSS licenses. + $(GO_LICENSES) check ./... --disallowed_types=forbidden,restricted,unknown + +.PHONY: check +check: generate manifests fmt lint vet lint-sh spellcheck test check-license check-dependency-license # Generate manifests, code, lint, fmt, test + +.PHONY: clean +clean: ## Remove all generated files (bin/, dist/, coverage files) + rm -rf bin/ + rm -rf dist/ + rm -f cover.out + +##@ Build + +.PHONY: docs +docs: crd-ref-docs ## Generate API reference documentation. + $(CRD_REF_DOCS) --source-path=./api --config=./hack/api-reference/config.yaml --renderer=markdown --output-path=./docs/api-reference/index.md + @sed -i.bak \ + -e 's/#networkingmetalironcoredevv1alpha1/#networking-metal-ironcore-dev-v1alpha1/g' \ + -e 's/#nxcisconetworkingmetalironcoredevv1alpha1/#nx-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ + -e 's/#xecisconetworkingmetalironcoredevv1alpha1/#xe-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ + -e 's/#xrcisconetworkingmetalironcoredevv1alpha1/#xr-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ + docs/api-reference/index.md + @find . -type f -name "*.bak" -delete -# 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_CLUSTER) || { \ - 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" - @KIND_CLUSTER=$(KIND_CLUSTER) 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" - @mkdir -p dist; 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" - @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_CLUSTER_NAME=$(KIND_CLUSTER) CONTAINER_TOOL=$(CONTAINER_TOOL) ./hack/kind-with-registry.sh +ROOT_DIR := $(shell pwd) +DOCS_IMG ?= ironcore-dev/network-operator-docs:latest -# 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)\e[0m\n" - KIND_EXPERIMENTAL_PROVIDER=$(CONTAINER_TOOL) kind delete cluster --name=$(KIND_CLUSTER) - $(CONTAINER_TOOL) stop kind-registry && $(CONTAINER_TOOL) rm kind-registry +.PHONY: run-docs +run-docs: + $(CONTAINER_TOOL) build -t $(DOCS_IMG) -f docs/Dockerfile docs --load + $(CONTAINER_TOOL) run --rm --init -p 5173:5173 -v $(ROOT_DIR)/docs:/workspace -v /workspace/node_modules $(DOCS_IMG) -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)\e[0m\n" - @tilt up --context kind-$(KIND_CLUSTER) +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + CGO_ENABLED=0 go build $(LDFLAGS) -o bin/manager cmd/main.go -manifests: generate +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/main.go -# Generate manifests e.g. CRD, RBAC etc. -charts: FORCE install-kubebuilder generate - @printf "\e[1;36m>> kubebuilder edit --plugins=helm/v2-alpha\e[0m\n" +.PHONY: helm +helm: kubebuilder @mv charts/network-operator charts/chart - @kubebuilder edit --plugins=helm/v2-alpha --output-dir=charts + $(KUBEBUILDER) edit --plugins=helm/v2-alpha --output-dir=charts @mv charts/chart charts/network-operator && rm -rf dist @# Fix cert-manager volumeMounts/volumes indentation (https://github.com/kubernetes-sigs/kubebuilder/issues/5677) - @$(SED) -i \ + @sed -i.bak \ -e '/certManager.enable/,/end/{s/^ - mountPath:/ - mountPath:/;s/^ name: webhook-certs/ name: webhook-certs/;s/^ readOnly: true/ readOnly: true/;s/^ - name: webhook-certs/ - name: webhook-certs/;s/^ secret:/ secret:/;s/^ secretName:/ secretName:/}' \ charts/network-operator/templates/manager/manager.yaml - -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 - -docker-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 - -docker-run-test-gnmi-server: FORCE docker-build-test-gnmi-server - @printf "\e[1;36m>> $(CONTAINER_TOOL) run -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG)\e[0m\n" + @find . -type f -name "*.bak" -delete + +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build \ + --platform=linux/$(GOARCH) \ + --build-arg=BUILD_DATE=$(BUILD_DATE) \ + --build-arg=GIT_COMMIT=$(GIT_COMMIT) \ + --build-arg=VERSION=$(VERSION) \ + -t $(IMG) . + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + @if ! grep -q "image: $(IMG)" config/manager/manager.yaml; then \ + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG); \ + fi + $(KUSTOMIZE) build config/default > dist/install.yaml + +TEST_SERVER_IMG ?= gnmi-test-server:latest + +.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/ + +.PHONY: run-test-gnmi-server +run-test-gnmi-server: build-test-gnmi-server ## Run the gnmi test server in a container. @$(CONTAINER_TOOL) run --rm -p 8000:8000 -p 9339:9339 $(TEST_SERVER_IMG) -# TEST_LAB_IMG defines the image to used for packaging the lab tests. -TEST_LAB_IMG ?= ghcr.io/ironcore-dev/network-operator-lab-test:latest - -test-lab: FORCE - @printf "\e[1;36m>> go test ./test/lab/ -v\e[0m\n" - @go test ./test/lab/ -v - -docker-build-test-lab: FORCE - @printf "\e[1;36m>> $(CONTAINER_TOOL) build --file=test/lab/Dockerfile --tag=$(TEST_LAB_IMG) .\e[0m\n" - @$(CONTAINER_TOOL) build --file=test/lab/Dockerfile --tag=$(TEST_LAB_IMG) . - -docker-push-test-lab: FORCE docker-push-test-lab - @printf "\e[1;36m>> $(CONTAINER_TOOL) push $(TEST_LAB_IMG)\e[0m\n" - @$(CONTAINER_TOOL) push $(TEST_LAB_IMG) - -ROOT_DIR := $(shell pwd) -DOCS_IMG ?= ironcore-dev/network-operator-docs:latest - -run-docs: - @docker build -t $(DOCS_IMG) -f docs/Dockerfile docs --load - @docker run --rm --init -p 5173:5173 -v $(ROOT_DIR)/docs:/workspace -v /workspace/node_modules $(DOCS_IMG) - -docs: install-crd-ref-docs - crd-ref-docs --source-path=./api --config=./hack/api-reference/config.yaml --renderer=markdown --output-path=./docs/api-reference/index.md - @$(SED) -i \ - -e 's/#networkingmetalironcoredevv1alpha1/#networking-metal-ironcore-dev-v1alpha1/g' \ - -e 's/#nxcisconetworkingmetalironcoredevv1alpha1/#nx-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ - -e 's/#xecisconetworkingmetalironcoredevv1alpha1/#xe-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ - -e 's/#xrcisconetworkingmetalironcoredevv1alpha1/#xr-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ - docs/api-reference/index.md - -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-shellcheck: FORCE - @set -eou pipefail; if ! hash shellcheck 2>/dev/null; then printf "\e[1;36m>> Installing shellcheck...\e[0m\n"; SHELLCHECK_ARCH=$$(uname -m); if [[ "$$SHELLCHECK_ARCH" == "arm64" ]]; then SHELLCHECK_ARCH=aarch64; fi; SHELLCHECK_OS=$$(uname -s | tr '[:upper:]' '[:lower:]'); 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 +##@ Deployment -install-typos: FORCE - @set -eou pipefail; if ! hash typos 2>/dev/null; then printf "\e[1;36m>> Installing typos...\e[0m\n"; TYPOS_ARCH=$$(uname -m); if [[ "$$TYPOS_ARCH" == "arm64" ]]; then TYPOS_ARCH=aarch64; fi; if command -v curl >/dev/null 2>&1; then GET="curl $${GITHUB_TOKEN:+" -u \":$$GITHUB_TOKEN\""} -sLo-"; elif command -v wget >/dev/null 2>&1; then GET="wget $${GITHUB_TOKEN:+" --password \"$$GITHUB_TOKEN\""} -O-"; else echo "Didn't find curl or wget to download typos"; exit 2; fi; if command -v gh >/dev/null; then TYPOS_GET_RELEASE_JSON="gh api /repos/crate-ci/typos/releases"; else TYPOS_GET_RELEASE_JSON="$$GET https://api.github.com/repos/crate-ci/typos/releases"; fi; TYPOS_VERSION=$$($$TYPOS_GET_RELEASE_JSON | jq -r '.[0].name' ); if [[ $(UNAME_S) == Darwin ]]; then TYPOS_FILE="typos-$$TYPOS_VERSION-$$TYPOS_ARCH-apple-darwin.tar.gz"; elif [[ $(UNAME_S) == Linux ]]; then TYPOS_FILE="typos-$$TYPOS_VERSION-$$TYPOS_ARCH-unknown-linux-musl.tar.gz"; fi; mkdir -p typos; $$GET ""https://github.com/crate-ci/typos/releases/download/$$TYPOS_VERSION/$$TYPOS_FILE"" | tar -C typos -zxf -; BIN=$$(go env GOBIN); if [[ -z $$BIN ]]; then BIN=$$(go env GOPATH)/bin; fi; install -Dm755 typos/typos -t "$$BIN"; rm -rf typos/; fi - -install-go-licence-detector: FORCE - @if ! hash go-licence-detector 2>/dev/null; then printf "\e[1;36m>> Installing go-licence-detector (this may take a while)...\e[0m\n"; go install go.elastic.co/go-licence-detector@latest; fi - -install-addlicense: FORCE - @if ! hash addlicense 2>/dev/null; then printf "\e[1;36m>> Installing addlicense (this may take a while)...\e[0m\n"; go install github.com/google/addlicense@latest; fi - -prepare-static-check: FORCE install-goimports install-golangci-lint install-shellcheck install-typos install-go-licence-detector install-addlicense +ifndef ignore-not-found + ignore-not-found = false +endif -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 +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster. + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - -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 +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster. 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 - -# To add additional flags or values (before the default ones), specify the variable in the environment, e.g. `GO_BUILDFLAGS='-tags experimental' make`. -# To override the default flags or values, specify the variable on the command line, e.g. `make GO_BUILDFLAGS='-tags experimental'`. -GO_BUILDFLAGS += -GO_LDFLAGS += -GO_TESTFLAGS += -GO_TESTENV += -GO_BUILDENV += CGO_ENABLED=0 +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster. + @if ! grep -q "image: $(IMG)" config/manager/manager.yaml; then \ + cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG); \ + fi + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - -# 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") +.PHONY: undeploy +undeploy: kustomize ## Undeploy controller from the K8s cluster. 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 - -build-all: build/network-operator +##@ Dependencies -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 +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) -DESTDIR = -ifeq ($(UNAME_S),Darwin) - PREFIX = /usr/local -else - PREFIX = /usr -endif +CURL_RETRIES=3 + +## Tool Binaries +KUBECTL ?= kubectl +KIND ?= $(LOCALBIN)/kind +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +KUBEBUILDER ?= $(LOCALBIN)/kubebuilder +CRD_REF_DOCS ?= $(LOCALBIN)/crd-ref-docs +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint +GOLANGCI_LINT_CUSTOM = $(LOCALBIN)/golangci-lint-custom +GOIMPORTS ?= $(LOCALBIN)/goimports +GOFUMPT ?= $(LOCALBIN)/gofumpt +ADDLICENSE ?= $(LOCALBIN)/addlicense +GO_LICENSES ?= $(LOCALBIN)/go-licenses +TYPOS ?= $(LOCALBIN)/typos +SHELLCHECK ?= $(LOCALBIN)/shellcheck +NETOP_PROVIDER ?= $(LOCALBIN)/netop-provider + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.8.1 +CONTROLLER_TOOLS_VERSION ?= v0.21.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, $$4}') +KUBEBUILDER_VERSION ?= v4.14.0 +CRD_REF_DOCS_VERSION ?= v0.3.0 +GOLANGCI_LINT_VERSION ?= v2.12.2 +GOIMPORTS_VERSION ?= v0.45.0 +GOFUMPT_VERSION ?= v0.10.0 +ADDLICENSE_VERSION ?= v1.2.0 +GO_LICENSES_VERSION ?= v2.0.1 +TYPOS_VERSION ?= v1.47.2 +SHELLCHECK_VERSION ?= v0.11.0 +KIND_VERSION ?= v0.32.0 + +.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; \ + } -install: FORCE build/network-operator - install -d -m 0755 "$(DESTDIR)$(PREFIX)/bin" - install -m 0755 build/network-operator "$(DESTDIR)$(PREFIX)/bin/network-operator" +.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: kubebuilder +kubebuilder: $(KUBEBUILDER) ## Download kubebuilder locally if necessary. +$(KUBEBUILDER): $(LOCALBIN) + $(call go-install-tool,$(KUBEBUILDER),sigs.k8s.io/kubebuilder/v4,$(KUBEBUILDER_VERSION)) + +.PHONY: crd-ref-docs +crd-ref-docs: $(CRD_REF_DOCS) ## Download crd-ref-docs locally if necessary. +$(CRD_REF_DOCS): $(LOCALBIN) + $(call go-install-tool,$(CRD_REF_DOCS),github.com/elastic/crd-ref-docs,$(CRD_REF_DOCS_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: golangci-lint-custom +golangci-lint-custom: $(GOLANGCI_LINT_CUSTOM) ## Build golangci-lint with custom plugins if .custom-gcl.yaml exists. +$(GOLANGCI_LINT_CUSTOM): $(GOLANGCI_LINT) .custom-gcl.yaml + @$(GOLANGCI_LINT) custom --destination $(LOCALBIN) --name golangci-lint-custom + +.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: gofumpt +gofumpt: $(GOFUMPT) ## Download gofumpt locally if necessary. +$(GOFUMPT): $(LOCALBIN) + $(call go-install-tool,$(GOFUMPT),mvdan.cc/gofumpt,$(GOFUMPT_VERSION)) + +.PHONY: addlicense +addlicense: $(ADDLICENSE) ## Download addlicense locally if necessary. +$(ADDLICENSE): $(LOCALBIN) + $(call go-install-tool,$(ADDLICENSE),github.com/google/addlicense,$(ADDLICENSE_VERSION)) + +.PHONY: go-licenses +go-licenses: $(GO_LICENSES) ## Download go-licenses locally if necessary. +$(GO_LICENSES): $(LOCALBIN) + $(call go-install-tool,$(GO_LICENSES),github.com/google/go-licenses,$(GO_LICENSES_VERSION)) + +TYPOS_OS := $(if $(filter darwin,$(GOOS)),apple-darwin,unknown-linux-musl) +RELEASE_ARCH := $(if $(filter amd64,$(GOARCH)),x86_64,$(if $(filter arm64,$(GOARCH)),aarch64,$(GOARCH))) + +.PHONY: typos +typos: $(TYPOS) ## Download typos locally if necessary. +$(TYPOS): $(LOCALBIN) + $(call download-tool,$(TYPOS),https://github.com/crate-ci/typos/releases/download/$(TYPOS_VERSION)/typos-$(TYPOS_VERSION)-$(RELEASE_ARCH)-$(TYPOS_OS).tar.gz,$(TYPOS_VERSION),-xzf - -C $(LOCALBIN) ./typos) + +.PHONY: shellcheck +shellcheck: $(SHELLCHECK) ## Download shellcheck locally if necessary. +$(SHELLCHECK): $(LOCALBIN) + $(call download-tool,$(SHELLCHECK),https://github.com/koalaman/shellcheck/releases/download/$(SHELLCHECK_VERSION)/shellcheck-$(SHELLCHECK_VERSION).$(GOOS).$(RELEASE_ARCH).tar.xz,$(SHELLCHECK_VERSION),-xJf - --strip-components=1 -C $(LOCALBIN) shellcheck-$(SHELLCHECK_VERSION)/shellcheck) + +.PHONY: kind +kind: $(KIND) ## Download kind locally if necessary. +$(KIND): $(LOCALBIN) + $(call go-install-tool,$(KIND),sigs.k8s.io/kind,$(KIND_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 + +# 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 + +# download-tool will download a release artifact from GitHub and extract it to LOCALBIN, if it doesn't exist +# $1 - target path with name of binary +# $2 - full download URL +# $3 - specific version +# $4 - tar flags and archive path (e.g. "-xzf - -C $(LOCALBIN) ./typos") +define download-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +echo "Downloading $(notdir $(1)) $(3)" ;\ +curl -sSLf $(2) | tar $(4) ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef + +## -------------------------------------- +## Tilt / Kind +## -------------------------------------- + +KIND_CLUSTER_NAME ?= network-operator + +.PHONY: kind-create +kind-create: kind ## Create the kind cluster if needed + KIND_CLUSTER_NAME=$(KIND_CLUSTER_NAME) CONTAINER_TOOL=$(CONTAINER_TOOL) ./hack/kind-with-registry.sh + +.PHONY: kind-delete +kind-delete: kind ## Destroys the kind cluster. + KIND_EXPERIMENTAL_PROVIDER=$(CONTAINER_TOOL) $(KIND) delete cluster --name=$(KIND_CLUSTER_NAME) + $(CONTAINER_TOOL) stop kind-registry && $(CONTAINER_TOOL) rm kind-registry -# which packages to test with test runner -GO_TESTPKGS := $(shell go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.Dir}}{{end}}' ./... | grep -Ev '/test') -ifeq ($(GO_TESTPKGS),) -GO_TESTPKGS := ./... -endif -# which packages to measure coverage for -GO_COVERPKGS := $(shell go list ./... | grep -E '/internal') -# to get around weird Makefile syntax restrictions, we need variables containing nothing, a space and comma -null := -space := $(null) $(null) -comma := , - -check: FORCE static-check build/cover.html build-all - @printf "\e[1;32m>> All checks successful.\e[0m\n" - -generate: install-controller-gen - @printf "\e[1;36m>> controller-gen\e[0m\n" - @controller-gen crd rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases - @controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..." - @controller-gen applyconfiguration: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-shellcheck: FORCE install-shellcheck - @printf "\e[1;36m>> shellcheck\e[0m\n" - @find . \( -path '*/docs/node_modules/*/*' -prune \) -o \( -path 'docs/node_modules/*' -prune \) -o -type f \( -name '*.bash' -o -name '*.ksh' -o -name '*.zsh' -o -name '*.sh' -o -name '*.shlib' \) -exec shellcheck {} + - -run-typos: FORCE install-typos - @printf "\e[1;36m>> typos\e[0m\n" - @typos - -build/cover.out: FORCE generate install-setup-envtest | build - @printf "\e[1;36m>> Running tests\e[0m\n" - KUBEBUILDER_ASSETS=$$(setup-envtest use 1.35 -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_TESTFLAGS) $(GO_TESTPKGS) - @awk < build/coverprofile.out '$$1 != "mode:" { is_filename[$$1] = true; counts1[$$1]+=$$2; counts2[$$1]+=$$3 } END { for (filename in is_filename) { printf "%s %d %d\n", filename, counts1[filename], counts2[filename]; } }' | sort | $(SED) '1s/^/mode: count\n/' > $@ - -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-license-headers: FORCE check-addlicense - -__static-check: FORCE run-shellcheck run-golangci-lint check-dependency-licenses check-license-headers - -static-check: FORCE - @$(MAKE) --keep-going --no-print-directory __static-check - -build: - @mkdir $@ - -tidy-deps: FORCE - go mod tidy - go mod verify - -license-headers: FORCE install-addlicense - @printf "\e[1;36m>> addlicense (for license headers on source code files)\e[0m\n" - @printf "%s\0" $(patsubst $(shell awk '$$1 == "module" {print $$2}' go.mod)%,.%/*.go,$(shell go list ./...)) | $(XARGS) -0 -I{} bash -c 'year="$$(grep 'Copyright' {} | head -n1 | grep -E -o '"'"'[0-9]{4}(-[0-9]{4})?'"'"')"; if [[ -z "$$year" ]]; then year=$$(date +%Y); fi; gawk -i inplace '"'"'{if (display) {print} else {!/^\/\*/ && !/^\*/}}; {if (!display && $$0 ~ /^(package |$$)/) {display=1} else { }}'"'"' {}; addlicense -c "SAP SE or an SAP affiliate company and IronCore contributors" -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[]' | sed '/\/d' | $(XARGS) reuse annotate -c 'SAP SE or an SAP affiliate company and IronCore contributors' -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 ./...)) - -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_TESTFLAGS=$(GO_TESTFLAGS)\n" - @printf "GO_TESTPKGS=$(GO_TESTPKGS)\n" - @printf "MAKE=$(MAKE)\n" - @printf "MAKE_VERSION=$(MAKE_VERSION)\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-shellcheck\e[0m Install shellcheck required by run-shellcheck/static-check\n" - @printf " \e[36minstall-typos\e[0m Install typos required by run-typos/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[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-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[36mrun-typos\e[0m Check for spelling errors using typos.\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-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[36mclean\e[0m Run git clean.\n" - -.PHONY: FORCE +.PHONY: tilt-up +tilt-up: $(KUSTOMIZE) kind-create ## Start tilt and create the kind cluster if needed + tilt up --context kind-$(KIND_CLUSTER_NAME) diff --git a/Makefile.maker.yaml b/Makefile.maker.yaml deleted file mode 100644 index aca9ebf97..000000000 --- a/Makefile.maker.yaml +++ /dev/null @@ -1,247 +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 - applyconfigurationHeaderFile: hack/boilerplate.go.txt - rbacRoleName: manager-role - -coverageTest: - only: "/internal" - -dockerfile: - # Custom Dockerfile using Distroless base image - enabled: false - -golang: - setGoModVersion: true - -golangciLint: - createConfig: false - skipDirs: - - internal/provider/openconfig - timeout: 10m - -shellCheck: - ignorePaths: - - 'docs/node_modules/*' - -goReleaser: - createConfig: true - -license: - addHeaders: true - checkDependencies: true - copyright: 'SAP SE or an SAP affiliate company and IronCore contributors' - spdx: Apache-2.0 - -nix: - enabled: false - -reuse: - # Custom REUSE.toml with minimal settings - enabled: false - -renovate: - enabled: true - assignees: - - felix-kaestner - -testPackages: - except: '/test' - -githubWorkflow: - ci: - enabled: false - license: - enabled: false - release: - enabled: false - securityChecks: - enabled: true - pushContainerToGhcr: - enabled: false - -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 defines the name of the Kind cluster to be used for the tilt setup. - KIND_CLUSTER ?= network - - LOCALBIN ?= $(shell pwd)/bin - $(LOCALBIN): - mkdir -p $(LOCALBIN) - - 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-kubebuilder: FORCE - @set -eou pipefail; if ! hash kubebuilder 2>/dev/null; then printf "\e[1;36m>> Installing kubebuilder...\e[0m\n"; 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 kubebuilder"; exit 2; fi; BIN=$$(go env GOBIN); if [[ -z $$BIN ]]; then BIN=$$(go env GOPATH)/bin; fi; $$GET "$$BIN/kubebuilder" "https://go.kubebuilder.io/dl/latest/$$(go env GOOS)/$$(go env GOARCH)"; chmod +x "$$BIN/kubebuilder"; 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 - - install-crd-ref-docs: FORCE - @if ! hash crd-ref-docs 2>/dev/null; then printf "\e[1;36m>> Installing crd-ref-docs...\e[0m\n"; go install github.com/elastic/crd-ref-docs@latest; fi - - bin/golangci-lint-custom: .custom-gcl.yaml - @hash golangci-lint 2>/dev/null || (printf "\e[1;31m>> golangci-lint not found, please install it first\e[0m\n"; exit 1) - golangci-lint custom --destination $(LOCALBIN) --name golangci-lint-custom - - lint: FORCE bin/golangci-lint-custom ## Run golangci-lint linter - @printf "\e[1;36m>> golangci-lint\e[0m\n" - @bin/golangci-lint-custom config verify - @bin/golangci-lint-custom run - - 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') - - # 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_CLUSTER) || { \ - 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" - @KIND_CLUSTER=$(KIND_CLUSTER) 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" - @mkdir -p dist; 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" - @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_CLUSTER_NAME=$(KIND_CLUSTER) CONTAINER_TOOL=$(CONTAINER_TOOL) ./hack/kind-with-registry.sh - - # 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)\e[0m\n" - KIND_EXPERIMENTAL_PROVIDER=$(CONTAINER_TOOL) kind delete cluster --name=$(KIND_CLUSTER) - $(CONTAINER_TOOL) stop kind-registry && $(CONTAINER_TOOL) rm kind-registry - - 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)\e[0m\n" - @tilt up --context kind-$(KIND_CLUSTER) - - manifests: generate - - # Generate manifests e.g. CRD, RBAC etc. - charts: FORCE install-kubebuilder generate - @printf "\e[1;36m>> kubebuilder edit --plugins=helm/v2-alpha\e[0m\n" - @mv charts/network-operator charts/chart - @kubebuilder edit --plugins=helm/v2-alpha --output-dir=charts - @mv charts/chart charts/network-operator && rm -rf dist - @# Fix cert-manager volumeMounts/volumes indentation (https://github.com/kubernetes-sigs/kubebuilder/issues/5677) - @$(SED) -i \ - -e '/certManager.enable/,/end/{s/^ - mountPath:/ - mountPath:/;s/^ name: webhook-certs/ name: webhook-certs/;s/^ readOnly: true/ readOnly: true/;s/^ - name: webhook-certs/ - name: webhook-certs/;s/^ secret:/ secret:/;s/^ secretName:/ secretName:/}' \ - charts/network-operator/templates/manager/manager.yaml - - 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 - - docker-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 - - docker-run-test-gnmi-server: FORCE docker-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) - - # TEST_LAB_IMG defines the image to used for packaging the lab tests. - TEST_LAB_IMG ?= ghcr.io/ironcore-dev/network-operator-lab-test:latest - - test-lab: FORCE - @printf "\e[1;36m>> go test ./test/lab/ -v\e[0m\n" - @go test ./test/lab/ -v - - docker-build-test-lab: FORCE - @printf "\e[1;36m>> $(CONTAINER_TOOL) build --file=test/lab/Dockerfile --tag=$(TEST_LAB_IMG) .\e[0m\n" - @$(CONTAINER_TOOL) build --file=test/lab/Dockerfile --tag=$(TEST_LAB_IMG) . - - docker-push-test-lab: FORCE docker-push-test-lab - @printf "\e[1;36m>> $(CONTAINER_TOOL) push $(TEST_LAB_IMG)\e[0m\n" - @$(CONTAINER_TOOL) push $(TEST_LAB_IMG) - - ROOT_DIR := $(shell pwd) - DOCS_IMG ?= ironcore-dev/network-operator-docs:latest - - run-docs: - @docker build -t $(DOCS_IMG) -f docs/Dockerfile docs --load - @docker run --rm --init -p 5173:5173 -v $(ROOT_DIR)/docs:/workspace -v /workspace/node_modules $(DOCS_IMG) - - docs: install-crd-ref-docs - crd-ref-docs --source-path=./api --config=./hack/api-reference/config.yaml --renderer=markdown --output-path=./docs/api-reference/index.md - @$(SED) -i \ - -e 's/#networkingmetalironcoredevv1alpha1/#networking-metal-ironcore-dev-v1alpha1/g' \ - -e 's/#nxcisconetworkingmetalironcoredevv1alpha1/#nx-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ - -e 's/#xecisconetworkingmetalironcoredevv1alpha1/#xe-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ - -e 's/#xrcisconetworkingmetalironcoredevv1alpha1/#xr-cisco-networking-metal-ironcore-dev-v1alpha1/g' \ - docs/api-reference/index.md diff --git a/README.md b/README.md index ef21b7da0..9fa96e2d3 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ Network-operator is a project built using Kubebuilder and controller-runtime to ### To Deploy on the cluster -**Build and push your image to the location specified by `IMG`:** +**Build your image to the tag specified by `IMG`:** ```sh -make docker-build docker-push IMG=/network-operator:tag +make docker-build IMG=/network-operator:tag ``` **NOTE:** This image ought to be published in the personal registry you specified. And it is required to have access to pull the image from the working environment. Make sure you have the proper permission to the registry if the above commands don’t work. @@ -38,7 +38,7 @@ make docker-build docker-push IMG=/network-operator:tag **Install the CRDs into the cluster:** ```sh -make deploy-crds +make install ``` **Deploy the Manager to the cluster with the image specified by `IMG`:** @@ -69,7 +69,7 @@ kubectl delete -k config/samples/ **Delete the APIs(CRDs) from the cluster:** ```sh -make undeploy-crds +make uninstall ``` **UnDeploy the controller from the cluster:** diff --git a/RELEASE.md b/RELEASE.md index 4ee6f9d59..72b1e9a6f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,5 @@ diff --git a/TESTING.md b/TESTING.md index 71ecd4d4e..c2cebee28 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,3 +1,8 @@ + + # Testing Guide This guide covers how the testing framework used for the `network-operator` project. @@ -14,17 +19,17 @@ The project uses three types of tests: ### Prerequisites -- Go 1.24+ +- Go 1.26+ - Make ### Running Unit Tests ```sh # Run all unit tests and collect coverage information -make build/cover.out +make coverage # Display coverage information in Browser -open build/cover.html # Use xdg-open on Linux +open cover.html # Use xdg-open on Linux ``` ### What Gets Tested @@ -46,14 +51,8 @@ open build/cover.html # Use xdg-open on Linux ### Running E2E Tests ```sh -# Create test cluster -make kind-create - # Run E2E tests make test-e2e - -# Cleanup (optional) -make kind-delete ``` ### What Gets Tested diff --git a/Tiltfile b/Tiltfile index b03e5885b..822c3ba18 100644 --- a/Tiltfile +++ b/Tiltfile @@ -7,20 +7,22 @@ analytics_settings(False) update_settings(k8s_upsert_timeout_secs=60) -allow_k8s_contexts(['minikube', 'kind-network']) +watch_settings(ignore=['**/*/zz_generated.deepcopy.go', 'config/crd/bases/*']) + +allow_k8s_contexts(['minikube', 'kind-network-operator']) load('ext://cert_manager', 'deploy_cert_manager') deploy_cert_manager(version='v1.18.2') -docker_build('ghcr.io/ironcore-dev/network-operator', '.', ignore=['config/crd/bases/*'], only=[ - 'api/', 'cmd/', 'hack/', 'internal/', 'go.mod', 'go.sum', 'Makefile', +docker_build('controller:latest', '.', only=[ + 'api/', 'cmd/', 'internal/', 'go.mod', 'go.sum' ]) -local_resource('controller-gen', 'make generate', ignore=['**/*/zz_generated.deepcopy.go', 'config/crd/bases/*'], deps=[ - 'api/', 'cmd/', 'hack/', 'internal/', 'go.mod', 'go.sum', 'Makefile', -]) +local_resource('controller-gen', 'make generate', deps=['api/', 'hack/boilerplate.go.txt']) + +local_resource('crds', 'make install', deps=['api/']) -docker_build('ghcr.io/ironcore-dev/gnmi-test-server:latest', './test/gnmi') +docker_build('gnmi-test-server:latest', './test/gnmi') provider = os.getenv('PROVIDER', 'openconfig') diff --git a/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go b/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go index da6230a60..bdea1227f 100644 --- a/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go +++ b/api/cisco/nx/v1alpha1/zz_generated.deepcopy.go @@ -1,6 +1,6 @@ //go:build !ignore_autogenerated -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors // SPDX-License-Identifier: Apache-2.0 // Code generated by controller-gen. DO NOT EDIT. diff --git a/api/core/v1alpha1/zz_generated.deepcopy.go b/api/core/v1alpha1/zz_generated.deepcopy.go index b3904a115..b17ff75b6 100644 --- a/api/core/v1alpha1/zz_generated.deepcopy.go +++ b/api/core/v1alpha1/zz_generated.deepcopy.go @@ -1,6 +1,6 @@ //go:build !ignore_autogenerated -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors // SPDX-License-Identifier: Apache-2.0 // Code generated by controller-gen. DO NOT EDIT. diff --git a/build/.gitignore b/build/.gitignore deleted file mode 100644 index b3fe57c0b..000000000 --- a/build/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors -# SPDX-License-Identifier: Apache-2.0 - -* -!.gitignore diff --git a/cmd/main.go b/cmd/main.go index 82de236ce..f16e086b8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,6 +7,7 @@ import ( "crypto/tls" "flag" "fmt" + "log" "os" "path/filepath" "strings" @@ -19,7 +20,6 @@ import ( // 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" coordinationv1 "k8s.io/api/coordination/v1" "k8s.io/apimachinery/pkg/runtime" @@ -58,6 +58,10 @@ import ( var ( scheme = runtime.NewScheme() setupLog = ctrl.Log.WithName("setup") + + version = "dev" + gitCommit = "none" + buildDate = "unknown" ) func init() { @@ -68,8 +72,13 @@ func init() { } func main() { //nolint:gocyclo - // if called with `--version`, report version and exit - bininfo.HandleVersionArgument() + if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "--version" || os.Args[1] == "-v") { + 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 diff --git a/config/develop/gnmi-test-server.yaml b/config/develop/gnmi-test-server.yaml index 789b76681..6fb617dbf 100644 --- a/config/develop/gnmi-test-server.yaml +++ b/config/develop/gnmi-test-server.yaml @@ -32,7 +32,7 @@ spec: type: RuntimeDefault containers: - name: gnmi-test-server - image: ghcr.io/ironcore-dev/gnmi-test-server:latest + image: gnmi-test-server:latest imagePullPolicy: IfNotPresent ports: - containerPort: 9339 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index b151545f2..4b0b5ae48 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -63,7 +63,7 @@ spec: args: - --leader-elect - --health-probe-bind-address=:8081 - image: ghcr.io/ironcore-dev/network-operator + image: controller:latest imagePullPolicy: IfNotPresent name: manager ports: [] diff --git a/go.mod b/go.mod index 5bc0df98a..7c519ecd3 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/openconfig/ygnmi v0.15.0 github.com/openconfig/ygot v0.34.0 github.com/pin/tftp/v3 v3.2.0 - github.com/sapcc/go-api-declarations v1.22.0 github.com/stretchr/testify v1.11.1 github.com/tidwall/gjson v1.19.0 go.uber.org/automaxprocs v1.6.0 diff --git a/go.sum b/go.sum index 25c3eed7a..1f4bf6fec 100644 --- a/go.sum +++ b/go.sum @@ -173,8 +173,6 @@ github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlT github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sapcc/go-api-declarations v1.22.0 h1:nU/eJ6OO54Z9YSo1gWinD0A2etrfZObCwYdB9xA0VWE= -github.com/sapcc/go-api-declarations v1.22.0/go.mod h1:x3V8bzg7Y4kmbA+DeWWwKteFEdCCSiVQdwRXj4fGAYY= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index 1fb47b55b..feda054a0 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,2 +1,2 @@ -// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-FileCopyrightText: YEAR SAP SE or an SAP affiliate company and IronCore contributors // SPDX-License-Identifier: Apache-2.0 diff --git a/hack/kind-with-registry.sh b/hack/kind-with-registry.sh index fabecd445..b18ad9373 100755 --- a/hack/kind-with-registry.sh +++ b/hack/kind-with-registry.sh @@ -9,8 +9,20 @@ CONTAINER_TOOL="${CONTAINER_TOOL:-docker}" KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-kind}" export KIND_EXPERIMENTAL_PROVIDER="${CONTAINER_TOOL}" +ROOTDIR=$(cd -- "$(dirname -- "$0")/.." && pwd) + +# Use local kind binary if available, otherwise fall back to global installation +if [ -x "$ROOTDIR/bin/kind" ]; then + KIND="$ROOTDIR/bin/kind" +elif command -v kind &>/dev/null; then + KIND="kind" +else + echo "Error: kind not found. Install it globally or run 'make kind' to download it locally." + exit 1 +fi + # Exit early if the cluster already exists -if kind get clusters | grep -q "^${KIND_CLUSTER_NAME}$"; then +if $KIND get clusters | grep -q "^${KIND_CLUSTER_NAME}$"; then echo "Cluster ${KIND_CLUSTER_NAME} already exists" exit 0 fi @@ -23,7 +35,7 @@ if [ "$("${CONTAINER_TOOL}" inspect -f '{{.State.Running}}' "${REGISTRY_NAME}" 2 fi # 2. Create kind cluster -cat </dev/null; then + KUSTOMIZE="kustomize" +else + echo "Error: kustomize not found. Install it globally or run 'make kustomize' to download it locally." + exit 1 +fi failed=0 while IFS= read -r -d '' kustomization; do dir=$(dirname "$kustomization") - name=${dir#"$BASEDIR/../"} - if kustomize build "$dir" >/dev/null 2>&1; then + name=${dir#"$ROOTDIR/"} + if $KUSTOMIZE build "$dir" >/dev/null 2>&1; then echo "OK: $name" else echo "FAILED: $name" failed=1 fi -done < <(find "$BASEDIR/../config" -name "kustomization.yaml" -print0) +done < <(find "$ROOTDIR/config" -name "kustomization.yaml" -print0) if [ "$failed" -ne 0 ]; then exit 1 diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 3702bb802..b413af888 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -28,11 +28,11 @@ var ( // image is the name of the image which will be build and loaded // with the code source changes to be tested. -const image = "ghcr.io/ironcore-dev/network-operator:latest" +const image = "controller:latest" // serverImage is the name of the image which will be built and loaded // with the gNMI test server. -const serverImage = "ghcr.io/ironcore-dev/gnmi-test-server:latest" +const serverImage = "gnmi-test-server:latest" // 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. @@ -62,7 +62,7 @@ var _ = BeforeSuite(func(ctx SpecContext) { ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") By("building the gnmi-test-server image") - cmd = exec.CommandContext(ctx, "make", "docker-build-test-gnmi-server", "TEST_SERVER_IMG="+serverImage) + cmd = exec.CommandContext(ctx, "make", "build-test-gnmi-server", "TEST_SERVER_IMG="+serverImage) _, err = Run(cmd) ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the gnmi-test-server image") diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index e583ec9cb..ba20616ee 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -80,7 +80,7 @@ var _ = Describe("Manager", Ordered, func() { Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") By("installing CRDs") - cmd = exec.CommandContext(ctx, "make", "deploy-crds") + cmd = exec.CommandContext(ctx, "make", "install") _, err = Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs") @@ -109,7 +109,7 @@ var _ = Describe("Manager", Ordered, func() { Expect(err).NotTo(HaveOccurred(), "Failed to undeploy the controller-manager") By("uninstalling CRDs") - cmd = exec.CommandContext(ctx, "make", "undeploy-crds") + cmd = exec.CommandContext(ctx, "make", "uninstall", "ignore-not-found=true") _, err = Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to uninstall CRDs")