diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000000..7fb00f820b5 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,42 @@ +# Security Policy + +## Security Announcements + +Join the [kubernetes-security-announce] group for security and vulnerability announcements related to the Kubernetes ecosystem. + +You can also subscribe to an RSS feed of these announcements using [this link][kubernetes-security-announce-rss]. + +## Reporting a Vulnerability + +Instructions for reporting a vulnerability can be found on the [Kubernetes Security and Disclosure Information] page. + +## Supported Versions + +Kubebuilder is tested against the latest three Kubernetes releases, in alignment with the [Kubernetes version and version skew support policy](https://kubernetes.io/docs/setup/release/version-skew-policy/). + +However, each version is only tested with the dependencies used for its release. For detailed information, please refer to the [compatibility and support policy on GitHub][compatibility-policy]. + +## Release Policy + +Kubebuilder maintains a policy of releasing updates for the latest CLI version (currently v4). Older versions (v1, v2, v3) are no longer supported, and no releases will be produced for them. It is recommended to ensure that any project scaffolded by Kubebuilder remains aligned with the latest release. + +## Automated Vulnerability Scanning + +Kubebuilder employs automated scanning via Dependabot and GitHub Actions within its CI/CD pipeline. This process detects vulnerabilities in dependencies and configurations, generating daily or weekly reports prioritized for the latest supported versions. + +- **Dependabot Configuration**: You can review the setup in `.github/dependabot.yml`. +- **Security Checks**: Security checks are enabled in the Kubebuilder repository settings. +- **Code Scanning**: The `.github/workflows/codeql.yml` workflow scans the `master` and `book-v4` branches, which typically contain the latest release code. Other release branches may not be scanned. + +## Production-Grade Security + +Projects generated by Kubebuilder are designed for ease of development and are **not** configured with production-grade security settings. For example, default configurations do not enable cert-manager or perform proper certificate validation, which may not be suitable for production environments. Ensure that you make the necessary adjustments to security settings before releasing your solution for production. + +[kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce +[kubernetes-security-announce-rss]: https://groups.google.com/forum/feed/kubernetes-security-announce/msgs/rss_v2_0.xml?num=50 +[Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions +[Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability +[compatibility-policy]: ./../README.md#versions-compatibility-and-supportability +[project-upgrade-assistant]: https://book.kubebuilder.io/reference/rescaffold +[testdata-directory]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata +[kubebuilder-releases]: https://github.com/kubernetes-sigs/kubebuilder/releases diff --git a/.github/workflows/apidiff.yml b/.github/workflows/apidiff.yml index 9f5bdabdbad..c9752d9cdd9 100644 --- a/.github/workflows/apidiff.yml +++ b/.github/workflows/apidiff.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "~1.22" + go-version-file: go.mod - name: Execute go-apidiff uses: joelanford/go-apidiff@v0.8.2 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f3bd673152c..40938041968 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version-file: go.mod - name: Build and install Kubebuilder CLI run: make install diff --git a/.github/workflows/external-plugin.yml b/.github/workflows/external-plugin.yml index d177dba8f16..71d6e04ee1d 100644 --- a/.github/workflows/external-plugin.yml +++ b/.github/workflows/external-plugin.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.22.3' + go-version-file: docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.mod - name: Build Sample External Plugin working-directory: docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1 diff --git a/.github/workflows/legacy-webhook-path.yml b/.github/workflows/legacy-webhook-path.yml index c094e23de76..880544d5986 100644 --- a/.github/workflows/legacy-webhook-path.yml +++ b/.github/workflows/legacy-webhook-path.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.22.3' + go-version-file: go.mod - name: Run make test-legacy run: make test-legacy diff --git a/.github/workflows/lint-sample.yml b/.github/workflows/lint-sample.yml index 1a4a3753cdb..a28c23bbbea 100644 --- a/.github/workflows/lint-sample.yml +++ b/.github/workflows/lint-sample.yml @@ -11,6 +11,13 @@ on: jobs: lint-samples: runs-on: ubuntu-latest + strategy: + matrix: + folder: [ + "testdata/project-v4", + "testdata/project-v4-with-plugins", + "testdata/project-v4-multigroup" + ] if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository) steps: - name: Clone the code @@ -18,16 +25,20 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod + - name: Prepare ${{ matrix.folder }} + working-directory: ${{ matrix.folder }} + run: go mod tidy + - name: Check linter configuration + working-directory: ${{ matrix.folder }} + run: make lint-config - name: Run linter uses: golangci/golangci-lint-action@v6 with: - version: v1.59 - working-directory: testdata/project-v4 - args: --config .golangci.yml ./... - - name: Run linter - uses: golangci/golangci-lint-action@v6 - with: - version: v1.59 - working-directory: testdata/project-v4-with-plugins + version: v1.62.2 + working-directory: ${{ matrix.folder }} args: --config .golangci.yml ./... + - name: Run linter via makefile target + working-directory: ${{ matrix.folder }} + run: make lint + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 76d9e378ab7..b041718c9fe 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,16 +15,18 @@ jobs: # Pull requests from the same repository won't trigger this checks as they were already triggered by the push if: (github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository) steps: + - name: Clone the code + uses: actions/checkout@v4 - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' - - name: Clone the code - uses: actions/checkout@v4 + go-version-file: go.mod + - name: Check linter configuration + run: make lint-config - name: Run linter uses: golangci/golangci-lint-action@v6 with: - version: v1.61 + version: v1.62.2 yamllint: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6c6747a8af..924904f5034 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Clean dist directory run: rm -rf dist || true - name: Install Syft to generate SBOMs diff --git a/.github/workflows/test-devcontainer.yaml b/.github/workflows/test-devcontainer.yml similarity index 93% rename from .github/workflows/test-devcontainer.yaml rename to .github/workflows/test-devcontainer.yml index 0da5e603abb..84765940b38 100644 --- a/.github/workflows/test-devcontainer.yaml +++ b/.github/workflows/test-devcontainer.yml @@ -15,10 +15,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup Go 1.22.x + - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22.x" + go-version-file: go.mod - name: Setup NodeJS 20.x uses: actions/setup-node@v4 diff --git a/.github/workflows/test-e2e-book.yml b/.github/workflows/test-e2e-book.yml index 1264d9a7502..7b3aac95daa 100644 --- a/.github/workflows/test-e2e-book.yml +++ b/.github/workflows/test-e2e-book.yml @@ -15,10 +15,16 @@ on: - '.github/workflows/test-e2e-book.yml' jobs: - e2e-getting-started: + e2e: runs-on: ubuntu-latest strategy: fail-fast: true + matrix: + folder: [ + "docs/book/src/getting-started/testdata/project", + "docs/book/src/cronjob-tutorial/testdata/project", + "docs/book/src/multiversion-tutorial/testdata/project" + ] if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository steps: - name: Checkout repository @@ -27,7 +33,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | @@ -41,67 +47,6 @@ jobs: - name: Create kind cluster run: kind create cluster - - name: Running make test-e2e for Getting Started tutorial sample - working-directory: docs/book/src/getting-started/testdata/project + - name: Running make test-e2e for ${{ matrix.folder }} + working-directory: ${{ matrix.folder }} run: make test-e2e - - e2e-cronjob-tutorial: - runs-on: ubuntu-latest - strategy: - fail-fast: true - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '~1.22' - - - name: Install the latest version of kind - run: | - curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - - name: Verify kind installation - run: kind version - - - name: Create kind cluster - run: kind create cluster - - - name: Running make test-e2e for Cronjob tutorial sample - working-directory: docs/book/src/cronjob-tutorial/testdata/project - run: make test-e2e - - e2e-multiversion-tutorial: - runs-on: ubuntu-latest - strategy: - fail-fast: true - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '~1.22' - - - name: Install the latest version of kind - run: | - curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - - name: Verify kind installation - run: kind version - - - name: Create kind cluster - run: kind create cluster - - - name: Running make test-e2e for Multiversion tutorial sample - working-directory: docs/book/src/multiversion-tutorial/testdata/project - run: make test-e2e - diff --git a/.github/workflows/test-e2e-samples.yml b/.github/workflows/test-e2e-samples.yml index 3865f8624ca..1e170ff60a8 100644 --- a/.github/workflows/test-e2e-samples.yml +++ b/.github/workflows/test-e2e-samples.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | @@ -41,7 +41,10 @@ jobs: run: | KUSTOMIZATION_FILE_PATH="testdata/project-v4/config/default/kustomization.yaml" sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH - sed -i '50,177s/^#//' $KUSTOMIZATION_FILE_PATH + sed -i '47,49s/^#//' $KUSTOMIZATION_FILE_PATH + # Uncomment all cert-manager injections + sed -i '59,212s/^#//' $KUSTOMIZATION_FILE_PATH + sed -i '214,229s/^#//' $KUSTOMIZATION_FILE_PATH cd testdata/project-v4/ go mod tidy @@ -62,7 +65,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | @@ -81,9 +84,12 @@ jobs: KUSTOMIZATION_FILE_PATH="testdata/project-v4-with-plugins/config/default/kustomization.yaml" sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH # Uncomment only ValidatingWebhookConfiguration - # from cert-manager replaces - sed -i '50,116s/^#//' $KUSTOMIZATION_FILE_PATH - sed -i '148,177s/^#//' $KUSTOMIZATION_FILE_PATH + # from cert-manager replaces; we are leaving defaulting uncommented + # since this sample has no defaulting webhooks + sed -i '59,164s/^#//' $KUSTOMIZATION_FILE_PATH + # Uncomment only --conversion webhooks CA injection + sed -i '197,212s/^#//' $KUSTOMIZATION_FILE_PATH + sed -i '214,229s/^#//' $KUSTOMIZATION_FILE_PATH cd testdata/project-v4-with-plugins/ go mod tidy @@ -104,7 +110,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | @@ -122,7 +128,10 @@ jobs: run: | KUSTOMIZATION_FILE_PATH="testdata/project-v4-multigroup/config/default/kustomization.yaml" sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH - sed -i '50,177s/^#//' $KUSTOMIZATION_FILE_PATH + # Uncomment all cert-manager injections for webhooks only + sed -i '59,59s/^#//' $KUSTOMIZATION_FILE_PATH + sed -i '98,212s/^#//' $KUSTOMIZATION_FILE_PATH + sed -i '214,229s/^#//' $KUSTOMIZATION_FILE_PATH cd testdata/project-v4-multigroup go mod tidy diff --git a/.github/workflows/test-helm-samples.yml b/.github/workflows/test-helm-samples.yml new file mode 100644 index 00000000000..94939ccf5ca --- /dev/null +++ b/.github/workflows/test-helm-samples.yml @@ -0,0 +1,90 @@ +name: Helm Testdata Sample + +on: + push: + paths: + - "testdata/project-v4-with-plugins/**" + - ".github/workflows/test-helm-samples.yml" + pull_request: + paths: + - "testdata/project-v4-with-plugins/**" + - ".github/workflows/test-helm-samples.yml" + +jobs: + helm-test-project-v4-with-plugins: + runs-on: ubuntu-latest + strategy: + fail-fast: true + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install the latest version of kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Create kind cluster + run: kind create cluster + + - name: Prepare project-v4-with-plugins + run: | + cd testdata/project-v4-with-plugins/ + go mod tidy + make docker-build IMG=project-v4-with-plugins:v0.1.0 + kind load docker-image project-v4-with-plugins:v0.1.0 + + - name: Install Helm + run: | + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + + - name: Verify Helm installation + run: helm version + + - name: Lint Helm chart for project-v4-with-plugins + run: | + helm lint testdata/project-v4-with-plugins/dist/chart + + - 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 cert-manager via Helm + run: | + helm repo add jetstack https://charts.jetstack.io + helm repo update + helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true + + - name: Wait for cert-manager to be ready + run: | + kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager + kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-cainjector + kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-webhook + + - name: Render Helm chart for project-v4-with-plugins + run: | + helm template testdata/project-v4-with-plugins/dist/chart --namespace=project-v4-with-plugins-system + + - name: Install Helm chart for project-v4-with-plugins + run: | + helm install my-release testdata/project-v4-with-plugins/dist/chart --create-namespace --namespace project-v4-with-plugins-system --set prometheus.enable=true + + - name: Check Helm release status + run: | + helm status my-release --namespace project-v4-with-plugins-system + + - name: Check Presence of ServiceMonitor + run: | + kubectl wait --namespace project-v4-with-plugins-system --for=jsonpath='{.kind}'=ServiceMonitor servicemonitor/project-v4-with-plugins-controller-manager-metrics-monitor diff --git a/.github/workflows/testdata.yml b/.github/workflows/testdata.yml index ef44c044656..b0fab3f57fb 100644 --- a/.github/workflows/testdata.yml +++ b/.github/workflows/testdata.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.22.3' + go-version-file: go.mod - name: Remove pre-installed kustomize # This step is needed as the following one tries to remove # kustomize for each test but has no permission to do so diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 98e4795751c..4019916ab13 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod # This step is needed as the following one tries to remove # kustomize for each test but has no permission to do so - name: Remove pre-installed kustomize @@ -56,7 +56,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version-file: go.mod - name: Generate the coverage output run: make test-coverage - name: Send the coverage output diff --git a/.golangci.yml b/.golangci.yml index f768e89d686..1ec4edba185 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -18,7 +18,12 @@ issues: linters-settings: govet: - enable=fieldalignment: true + enable-all: true + disable: + - fieldalignment + - shadow + nolintlint: + allow-unused: false revive: rules: # The following rules are recommended https://github.com/mgechev/revive#recommended-configuration @@ -28,19 +33,16 @@ linters-settings: - name: dot-imports arguments: # dot import should be ONLY allowed for ginkgo testing packages - allowedPackages: - - "github.com/onsi/ginkgo/v2" - - "github.com/onsi/gomega" + - allowedPackages: + - "github.com/onsi/ginkgo/v2" + - "github.com/onsi/gomega" - name: error-return - name: error-strings - name: error-naming - name: exported - disabled: true # TODO: Investigate if it should be enabled. Disabled for now due to many findings. - name: if-return - disabled: true # TODO: Investigate if it should be enabled. Disabled for now due to many findings. - name: increment-decrement - name: var-naming - disabled: true # TODO: Investigate if it should be enabled. Disabled for now due to many findings. - name: var-declaration - name: package-comments disabled: true # TODO: Investigate if it should be enabled. Disabled for now due to many findings. @@ -51,7 +53,6 @@ linters-settings: - name: indent-error-flow - name: errorf - name: empty-block - disabled: true # TODO: Investigate if it should be enabled. Disabled for now due to many findings. - name: superfluous-else - name: unused-parameter - name: unreachable-code @@ -79,6 +80,7 @@ linters: - ineffassign - lll - misspell + - nolintlint - nakedret - prealloc - revive @@ -87,3 +89,4 @@ linters: - unconvert - unparam - unused + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8dd61dd7f8f..d94eaced5ed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Please see https://git.k8s.io/community/CLA.md for more info. ## Prerequisites -- [go](https://golang.org/dl/) version v1.22+. +- [go](https://golang.org/dl/) version v1.23+. - [docker](https://docs.docker.com/install/) version 17.03+. - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) version v1.11.3+. - [kustomize](https://github.com/kubernetes-sigs/kustomize/blob/master/site/content/en/docs/Getting%20started/installation.md) v3.1.0+ diff --git a/Makefile b/Makefile index ff51b14e5ba..0604fc547ff 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,12 @@ generate: generate-testdata generate-docs ## Update/generate all mock data. You .PHONY: remove-spaces remove-spaces: @echo "Removing trailing spaces" - @find . -type f -name "*.md" -exec sed -i '' 's/[[:space:]]*$$//' {} + || true + @bash -c ' \ + if [[ "$$(uname)" == "Linux" ]]; then \ + find . -type f -name "*.md" -exec sed -i "s/[[:space:]]*$$//" {} + || true; \ + else \ + find . -type f -name "*.md" -exec sed -i "" "s/[[:space:]]*$$//" {} + || true; \ + fi' .PHONY: generate-testdata generate-testdata: ## Update/generate the testdata in $GOPATH/src/sigs.k8s.io/kubebuilder @@ -80,9 +85,14 @@ generate-testdata: ## Update/generate the testdata in $GOPATH/src/sigs.k8s.io/ku ./test/testdata/generate.sh .PHONY: generate-docs -generate-docs: ## Update/generate the docs in $GOPATH/src/sigs.k8s.io/kubebuilder +generate-docs: ## Update/generate the docs ./hack/docs/generate.sh +.PHONY: generate-charts +generate-charts: build ## Re-generate the helm chart testdata only + rm -rf testdata/project-v4-with-plugins/dist/chart + (cd testdata/project-v4-with-plugins && ../../bin/kubebuilder edit --plugins=helm/v1-alpha) + .PHONY: check-docs check-docs: ## Run the script to ensure that the docs are updated ./hack/docs/check.sh @@ -95,16 +105,20 @@ lint: golangci-lint yamllint ## Run golangci-lint linter & yamllint 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: yamllint yamllint: - @files=$$(find testdata -name '*.yaml' ! -path 'testdata/*/dist/install.yaml'); \ + @files=$$(find testdata -name '*.yaml' ! -path 'testdata/*/dist/*'); \ docker run --rm $$(tty -s && echo "-it" || echo) -v $(PWD):/data cytopia/yamllint:latest $$files -d "{extends: relaxed, rules: {line-length: {max: 120}}}" --no-warnings GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint golangci-lint: @[ -f $(GOLANGCI_LINT) ] || { \ set -e ;\ - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) v1.61.0 ;\ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) v1.62.2 ;\ } .PHONY: apidiff @@ -171,3 +185,11 @@ test-spaces: ## Run the trailing spaces check test-legacy: ## Run the tests to validate legacy path for webhooks rm -rf ./testdata/**legacy**/ ./test/testdata/legacy-webhook-path.sh + +.PHONY: install-helm +install-helm: ## Install the latest version of Helm locally + @curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + +.PHONY: helm-lint +helm-lint: install-helm ## Lint the Helm chart in testdata + helm lint testdata/project-v4-with-plugins/dist/chart diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES index 6a5baaf3200..2bcbb49ffb3 100644 --- a/OWNERS_ALIASES +++ b/OWNERS_ALIASES @@ -15,7 +15,6 @@ aliases: # approvers & admins -- those count too via the OWNERS file) kubebuilder-reviewers: - rashmigottipati - - everettraven # folks who may have context on ancient history, # but are no longer directly involved @@ -28,3 +27,6 @@ aliases: - joelanford - mengqiy - pwittrock + + kubebuilder-emeritus-reviewers: + - everettraven diff --git a/README.md b/README.md index 8a250827573..493d70c4c03 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,18 @@ +> ⚠️ **IMPORTANT NOTICE:** Images under `gcr.io/kubebuilder/` Will Be Unavailable Soon +> +> **If your project uses `gcr.io/kubebuilder/kube-rbac-proxy`** it will be affected. +> Your project may fail to work if the image cannot be pulled. **You must move as soon as possible**, sometime from early 2025, the GCR will go away. +> +> The usage of the project [kube-rbac-proxy](https://github.com/brancz/kube-rbac-proxy) was discontinued from Kubebuilder +> and replaced for similar protection using `authn/authz` via Controller-Runtime's feature [WithAuthenticationAndAuthorization](https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/filters#WithAuthenticationAndAuthorization). +> +> For more information and guidance see the discussion https://github.com/kubernetes-sigs/kubebuilder/discussions/3907 + [![Lint](https://github.com/kubernetes-sigs/kubebuilder/actions/workflows/lint.yml/badge.svg)](https://github.com/kubernetes-sigs/kubebuilder/actions/workflows/lint.yml) [![Unit tests](https://github.com/kubernetes-sigs/kubebuilder/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/kubernetes-sigs/kubebuilder/actions/workflows/unit-tests.yml) [![Go Report Card](https://goreportcard.com/badge/sigs.k8s.io/kubebuilder)](https://goreportcard.com/report/sigs.k8s.io/kubebuilder) [![Coverage Status](https://coveralls.io/repos/github/kubernetes-sigs/kubebuilder/badge.svg?branch=master)](https://coveralls.io/github/kubernetes-sigs/kubebuilder?branch=master) -[![Latest release](https://badgen.net/github/release/kubernetes-sigs/kubebuilder)](https://github.com/kubernetes-sigs/kubebuilder/lreleases) +[![Latest release](https://badgen.net/github/release/kubernetes-sigs/kubebuilder)](https://github.com/kubernetes-sigs/kubebuilder/releases) ## Kubebuilder @@ -166,5 +176,5 @@ Additionally, we can use this channel to demonstrate new features. [operator-sdk]: https://github.com/operator-framework/operator-sdk [plugin-section]: https://book.kubebuilder.io/plugins/plugins.html [controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime -[your-own-plugins]: https://book.kubebuilder.io/plugins/creating-plugins.html +[your-own-plugins]: https://book.kubebuilder.io/plugins/extending [controller-tools]: https://github.com/kubernetes-sigs/controller-tools diff --git a/cmd/main.go b/cmd/main.go index 64a25502cd7..35e9db150aa 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -25,11 +25,10 @@ import ( "sigs.k8s.io/kubebuilder/v4/pkg/plugin" kustomizecommonv2 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/common/kustomize/v2" "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang" - - //nolint:staticcheck deployimagev1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1" golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4" grafanav1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/grafana/v1alpha" + helmv1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v1alpha" ) func init() { @@ -38,7 +37,7 @@ func init() { } func main() { - // Bundle plugin which built the golang projects scaffold by Kubebuilder go/v4 with kustomize v2 + // Bundle plugin which built the golang projects scaffold with base.go/v4 and kustomize/v2 plugins gov4Bundle, _ := plugin.NewBundleWithOptions(plugin.WithName(golang.DefaultNameQualifier), plugin.WithVersion(plugin.Version{Number: 4}), plugin.WithPlugins(kustomizecommonv2.Plugin{}, golangv4.Plugin{}), @@ -61,6 +60,7 @@ func main() { &kustomizecommonv2.Plugin{}, &deployimagev1alpha1.Plugin{}, &grafanav1alpha1.Plugin{}, + &helmv1alpha1.Plugin{}, ), cli.WithPlugins(externalPlugins...), cli.WithDefaultPlugins(cfgv3.Version, gov4Bundle), diff --git a/designs/README.md b/designs/README.md index 13750b3ca9d..fbeac49abe5 100644 --- a/designs/README.md +++ b/designs/README.md @@ -30,7 +30,7 @@ why things changed. For example: # Out of Date -This change is out of date. It turns out curly braces a frustrating to +This change is out of date. It turns out curly braces are frustrating to type, so we had to abandon functions entirely, and have users specify custom functionality using strings of Common LISP instead. See #000 for more information. diff --git a/designs/code-generate-image-plugin.md b/designs/code-generate-image-plugin.md index bb2634f6744..6dbf78b7d52 100644 --- a/designs/code-generate-image-plugin.md +++ b/designs/code-generate-image-plugin.md @@ -251,7 +251,7 @@ type {{ resource }}Spec struct { ### Test Plan -To ensure this implementation a new project example should be generated in the [testdata](../testdata/) directory of the project. See the [test/testadata/generate.sh](../test/testadata/generate.sh). Also, we should use this scaffold in the [integration tests](../test/e2e/) to ensure that the data scaffolded with works on the cluster as expected. +To ensure this implementation a new project example should be generated in the [testdata](../testdata/) directory of the project. See the [test/testdata/generate.sh](../test/testadata/generate.sh). Also, we should use this scaffold in the [integration tests](../test/e2e/) to ensure that the data scaffolded with works on the cluster as expected. ### Graduation Criteria diff --git a/designs/discontinue_usage_of_kube_rbac_proxy.md b/designs/discontinue_usage_of_kube_rbac_proxy.md index ca8badc4354..b4efa6b41d3 100644 --- a/designs/discontinue_usage_of_kube_rbac_proxy.md +++ b/designs/discontinue_usage_of_kube_rbac_proxy.md @@ -5,79 +5,79 @@ # Discontinue Kube RBAC Proxy in Default Kubebuilder Scaffolding This proposal highlights the need to reassess the usage of [kube-rbac-proxy](https://github.com/brancz/kube-rbac-proxy) -in the default scaffold due to the evolving k8s infra, community feedback. Key considerations include the transition to a shared infrastructure requiring +in the default scaffold due to the evolving k8s infra and community feedback. Key considerations include the transition to a shared infrastructure requiring all images to be published on [registry.k8s.io][registry.k8s.io], the deprecation of Google Cloud Platform's [Container Registry](https://cloud.google.com/artifact-registry/docs/transition/prepare-gcr-shutdown), and the fact -that [kube-rbac-proxy][kube-rbac-proxy] is not yet part of the Kubernetes ecosystem umbrella. +that [kube-rbac-proxy][kube-rbac-proxy] is yet to be part of the Kubernetes ecosystem umbrella. The dependency on a potentially discontinuable Google infrastructure, **which is out of our control**, paired with the challenges of maintaining, building, or promoting [kube-rbac-proxy][kube-rbac-proxy] images, calls for a change. -In this document is proposed replacing the [kube-rbac-proxy][kube-rbac-proxy] within +In this document is proposed to replace the [kube-rbac-proxy][kube-rbac-proxy] within [Network Policies][k8s-doc-networkpolicies] follow-up for potentially enhancements to protect the metrics endpoint combined with [cert-manager][cert-manager] and a new -feature introduced in controller-runtime, see [here][cr-pr]. +a feature introduced in controller-runtime, see [here][cr-pr]. **For the future (when kube-rbac-proxy be part of the k8s umbrella)**, it is proposed the usage of the [Plugins API provided by Kubebuilder](./../docs/book/src/plugins/plugins.md), to create an [external plugin](./../docs/book/src/plugins/creating-plugins.md) -to properly integrate the solution with Kubebuilder and provide a helper to allow users to opt-in as please them. +to properly integrate the solution with Kubebuilder and provide a helper to allow users to opt-in as they please them. ## Open Questions -- 1) [Network Policies][k8s-doc-networkpolicies] is implemented by the cluster’s CNI. Are we confident that the proposed policy is supported by all the major CNIs in use? +- 1) [Network Policies][k8s-doc-networkpolicies] is implemented by the cluster’s CNI. Are we confident that all the major CNIs in use support the proposed policy? > Besides [Network Policies][k8s-doc-networkpolicies] being part of the core Kubernetes API, their enforcement relies on the CNI plugin installed in -the Kubernetes cluster. While support and implementation details can vary among CNIs, it seems that the most commonly used ones, -such as Calico, Cilium, WeaveNet, and Canal, shows to offer support for NetworkPolicies. +the Kubernetes cluster. While support and implementation details vary among CNIs, the most commonly used ones, +such as Calico, Cilium, WeaveNet, and Canal, offer support for NetworkPolicies. > >Also, there was concern in the past because AWS did not support it. However, this changed, >as detailed in their announcement: [Amazon VPC CNI now supports Kubernetes Network Policies](https://aws.amazon.com/blogs/containers/amazon-vpc-cni-now-supports-kubernetes-network-policies/). > ->Moreover, under this proposal users still able to disable/enable this option as please them. +>Moreover, under this proposal, users can still disable/enable this option as they please them. -- 2) NetworkPolicy is a simple firewall and does not provide authn/authz and encryption. +- 2) NetworkPolicy is a simple firewall and does not provide `authn/authz` and encryption. > Yes, that's correct. NetworkPolicy acts as a basic firewall for pods within a Kubernetes cluster, controlling traffic > flow at the IP address or port level. However, it doesn't handle authentication (authn), authorization (authz), > or encryption directly like kube-rbac-proxy solution. > -> However, if we be able to combine the cert-manager and the new feature provided -> by controller-runtime we can achieve the same or a superior level of protection +> However, if we can combine the cert-manager and the new feature provided +> by controller-runtime, we can achieve the same or a superior level of protection > without relying on any extra third-party dependency. - 3) Could not Kubebuilder maintainers use the shared infrastructure to continue building and promoting those images under the new `registry.k8s.io`? > We tried to do that, see [here](https://github.com/kubernetes/test-infra/blob/master/config/jobs/image-pushing/k8s-staging-kubebuilder.yaml) the recipe implemented. > However, it does not work because kube-rbac-proxy is not under the -> kubernetes umbrella. More over we experiment the GitHub Repository as alternative approach, see the [PR](https://github.com/kubernetes-sigs/kubebuilder/pull/3854) but seems +> kubernetes umbrella. Moreover, we experimented with the GitHub Repository as an alternative approach, see the [PR](https://github.com/kubernetes-sigs/kubebuilder/pull/3854) but seems > that we are not allowed to use it. Nevertheless, neither approach sorts out all motivations and requirements -> Ideally, Kubebuilder should not be responsible for maintain and promote third-part artifacts. +> Ideally, Kubebuilder should not be responsible for maintaining and promoting third-party artefacts. - 4) However, is not Kubebuilder also building and promoting the binaries required to be used within [EnvTest](./../docs/book/src/reference/envtest.md) feature implemented in controller-runtime? > Yes, but it also will need to change. Controller-runtime maintainers are looking for solutions to -> built those binaries inside its project since it seems part of its domain. This change is likely +> build those binaries inside its project since it seems part of its domain. This change is likely > to be transparent to the community users. - 5) Could we not use the Controller-Runtime feature [controller-runtime][cr-pr] which enable secure metrics serving over HTTPS? -Yes, after some changes be address. After we ask for a hand for reviews from skilled auth maintainers and receiving feedback, it appears that this configuration does not +Yes, after some changes are addressed. After we ask for a hand for reviews from skilled auth maintainers and receive feedback, it appears that this configuration needs to align with best practices. See the [issue](https://github.com/kubernetes-sigs/controller-runtime/issues/2781) raised to track this need. - 6) Could we not make [cert-manager][cert-manager] mandatory? -> No, we can not. One of the goals of kubebuilder is to make it easier for new -users. So, we cannot make mandatory the usage of a third-part as cert-manager +> No, we can not. One of the goals of Kubebuilder is to make it easier for new +users. So, we cannot make mandatory the usage of a third party as cert-manager for users by default and to only quick-start. > > However, we can make mandatory the usage of [cert-manager][cert-manager] for some specific features like use kube-rbac-proxy -or as it is today to use webhooks which is a more advance and optional option. +or, as it is today, using webhooks, a more advanced and optional option. ## Summary @@ -100,16 +100,16 @@ at their discretion, poses a risk to image availability and project reliability. the [Container Registry][container-registry-dep] deprecation implies that **all** images provided so far by Kubebuilder [here][kb-images-repo] will unassailable by **April 22, 2025**. [More info][container-registry-dep] and [slack ETA thread][slack-eta-thread] - **Security and Endorsement Concerns**: [kube-rbac-proxy][kube-rbac-proxy] is a process to be part of -auth-sig for a long period, however, it not there yet. The Kubernetes Auth SIG’s review reveals that kube-rbac-proxy +auth-sig for an extended period, however, it is not there yet. The Kubernetes Auth SIG’s review reveals that kube-rbac-proxy must undergo significant updates to secure an official endorsement and to be supported, highlighting pressing concerns. You can check the ongoing process and changes required by looking at the [project issue](https://github.com/brancz/kube-rbac-proxy/issues/238) - **Evolving User Requirements and Deprecations**: The anticipated requirement for certificate management, potentially necessitating cert-manager, underlines Kubebuilder's aim to simplify setup and reduce third-party dependencies. [More info, see issue #3524](https://github.com/kubernetes-sigs/kubebuilder/issues/3524) -- **Aim for a Transparent and collaborative infrastructure**: As an open-source project, Kubebuilder strives for +- **Aim for a Transparent and Collaborative Infrastructure**: As an open-source project, Kubebuilder strives for a community-transparent infrastructure that allows broader contributions. This goal aligns with our initiative to migrate Kubebuilder CLI release builds from GCP to GitHub Actions and using Go-Releaser see [here](./../build/.goreleaser.yml), or promoting infrastructure managed under the k8s-infra umbrella. -- **Community Feedback**: Some community members expressing a preference for its removal from the default scaffolding. [Issue 3482](https://github.com/kubernetes-sigs/kubebuilder/issues/3482) +- **Community Feedback**: Some community members preferred its removal from the default scaffolding. [Issue 3482](https://github.com/kubernetes-sigs/kubebuilder/issues/3482) - **Enhancing Service Monitor with Proper TLS/Certificate Usage Requested by Community:** [Issue #3657](https://github.com/kubernetes-sigs/kubebuilder/issues/3657). It is achievable with [kube-rbac-proxy][kube-rbac-proxy] OR [Network Policies][k8s-doc-networkpolicies] usage within [cert-manager][cert-manager]. ### Goals @@ -118,26 +118,26 @@ or promoting infrastructure managed under the k8s-infra umbrella. protection achievable for the metrics endpoint without relying on new third-party dependencies or the need to build and promote images from other projects. - **Avoid Breaking Changes**: Ensure that users who generated projects with previous versions can still use the -new version with scaffold changes and are allowed to adapt their project at their convenience. +new version with scaffold changes and adapt their project at their convenience. - **Sustainable Project Maintenance**: Ensure all projects scaffolded by Kubebuilder can be maintained and supported by its maintainers. - **Independence from Google Cloud Platform**: Move away from reliance on Google Cloud Platform, considering the potential for unilateral shutdowns. - **Kubernetes Umbrella Compliance**: Cease the promotion or endorsement of solutions -not yet endorsed by the Kubernetes umbrella organization mainly when it is used and shipped with the workload itself. +not yet endorsed by the Kubernetes umbrella organization, mainly when used and shipped with the workload. - **Promote Use of External Plugins**: Adhere to Kubebuilder's directive to avoid direct third-party -integrations, favoring the support of projects through the Kubebuilder API and [external plugins][external-plugins]. +integrations, favouring the support of projects through the Kubebuilder API and [external plugins][external-plugins]. This approach empowers users to add or integrate solutions with the Kubebuilder scaffold on their own, ensuring that third-party project maintainers—who are more familiar with their solutions—can maintain and update their integrations, as implementing it following the best practices to use their project, enhancing the user experience. External plugins should reside within third-party repository solutions and remain up-to-date as part of those changes, aligning with their domain of responsibility. - **Flexible Network Policy Usage**: Allow users to opt-out of the default-enabled usage of [Network Policies][k8s-doc-networkpolicies] -if they prefer another solution, plan to deploy their solution with a vendor, or use a CNI that does not support NetworkPolicies. +if they prefer another solution, plan to deploy their solution with a vendor or use a CNI that does not support NetworkPolicies. ### Non-Goals -- **Replicate kube-rbac-proxy Features or Protection Level**: It is not a goal to provide exactly the same features +- **Replicate kube-rbac-proxy Features or Protection Level**: It is not a goal to provide the same features or layer of protection as [kube-rbac-proxy][kube-rbac-proxy]. Since [Network Policies][k8s-doc-networkpolicies]operate differently and do not offer the same kind of functionality as [kube-rbac-proxy][kube-rbac-proxy], achieving identical protection levels through [Network Policies][k8s-doc-networkpolicies]alone is not feasible. @@ -148,12 +148,12 @@ kube-rbac-proxy handles. ## Proposal -### Phase 1: Transition to NetworkPolicies +### Phase 1: Transition to network policies The immediate action outlined in this proposal is the replacement of [kube-rbac-proxy][kube-rbac-proxy] with Kubernetes API NetworkPolicies. -### Phase 2: Add Cert-Manager as Optional option to be used with metrics +### Phase 2: Add Cert-Manager as an Optional option to be used with metrics Looking beyond the initial phase, this proposal envisions integrating cert-manager for TLS certificate management and exploring synergies with new features in Controller Runtime, as demonstrated in [PR #2407](https://github.com/kubernetes-sigs/controller-runtime/pull/2407). @@ -162,18 +162,18 @@ These enhancements would introduce encrypted communication for metrics endpoints significantly elevating the security model employed by projects scaffolded by Kubebuilder. - **cert-manager**: Automates the management and issuance of TLS certificates, facilitating encrypted communication and, when configured with mTLS, adding a layer of authentication. - Currently, we leverage on cert-manager when webhooks are scaffold. So, the proposal idea would be to allow users enable the cert-manager for the metrics such as it is provided - and required for webhook feature. However, it MUST be optional. One of the goals of Kubebuilder is make it easier for new users, therefore new users should - not need to deal with cert-manager by default or have the need to install it to just quick start. + Currently, we leverage cert-manager when webhooks are scaffolded. So, the proposal idea would be to allow users to enable the cert-manager for the metrics such as those provided + and required for the webhook feature. However, it MUST be optional. One of the goals of Kubebuilder is to make it easier for new users. Therefore, new users should + not need to deal with cert-manager by default or have the need to install it to just a quick start. That would mean, in a follow-up to the [current open PR](https://github.com/kubernetes-sigs/kubebuilder/pull/3853) to address the above `phase 1 - Transition to NetworkPolices`, we aim to introduce a configurable Kustomize patch that will enable patching the ServiceMonitor in `config/prometheus/monitor.yaml` and certificates similar to our -existing setup for webhooks. This enhancement will ensure more flexible deployment configurations and enhance security +existing setup for webhooks. This enhancement will ensure more flexible deployment configurations and enhance the security features of the service monitoring components. Currently, in the `config/default/`, we have implemented patches for cert-manager along with webhooks, as seen in `config/default/kustomization.yaml` ([example](https://github.com/kubernetes-sigs/kubebuilder/blob/bd0876b8132ff66da12d8d8a0fdc701fde00f54b/docs/book/src/component-config-tutorial/testdata/project/config/default/kustomization.yaml#L51-L149)). -These patches handle annotations for the cert-manager CA injection across various configurations like +These patches handle annotations for the cert-manager CA injection across various configurations, like ValidatingWebhookConfiguration, MutatingWebhookConfiguration, and CRDs. For the proposed enhancements, we need to integrate similar configurations for the ServiceMonitor. @@ -224,10 +224,10 @@ spec: control-plane: controller-manager ``` -### Phase 3: When Controller-Runtime feature be enhanced +### Phase 3: When Controller-Runtime feature is enhanced After we have the [issue](https://github.com/kubernetes-sigs/controller-runtime/issues/2781) -addressed we plan to use it to protect the endpoint. See that would mean ensure +addressed, and we plan to use it to protect the endpoint. See, that would mean ensuring that we are either `handle authentication (authn), authorization (authz)`. Examples of its implementation can be found [here](https://github.com/kubernetes-sigs/cluster-api/blob/v1.6.3/util/flags/diagnostics.go#L79-L82). @@ -245,11 +245,11 @@ allowing users to run: kubebuilder init|edit --plugins="kube-rbac-proxy/v1" ``` -So that, the plugin could use the [plugin/util](../pkg/plugin/util) lib provide +So that the plugin could use the [plugin/util](../pkg/plugin/util) lib provide to comment (We can add a method like the [UncommentCode](https://github.com/kubernetes-sigs/kubebuilder/blob/72586d386cfbcaecea6321a703d1d7560c521885/pkg/plugin/util/util.go#L102)) the patches in the `config/default/kustomization` and disable the default network policy used within and [replace the code](https://github.com/kubernetes-sigs/kubebuilder/blob/72586d386cfbcaecea6321a703d1d7560c521885/pkg/plugin/util/util.go#L231) -in the `main.go` bellow with in order to not use the controller-runtime +in the `main.go` bellow with to not use the controller-runtime feature instead. ```go @@ -262,9 +262,9 @@ ctrlOptions := ctrl.Options{ ### Documentation Updates Each phase of implementation associated with this proposal must include corresponding -updates to the documentation. This is essential to ensure that end users understand +updates to the documentation. This is essential to ensure end users understand how to enable, configure, and utilize the options effectively. Documentation updates should be -committed as part of the pull request that introduces code changes. +completed as part of the pull request to introduce code changes. ### Proof of Concept @@ -275,19 +275,19 @@ committed as part of the pull request that introduces code changes. #### Loss of Previously Promoted Images -The transition to the new shared infrastructure for Kubernetes SIG projects has rendered us unable to automatically -build and promote images as before. The process only works for projects under the umbrella. +The transition to the new shared infrastructure for Kubernetes SIG projects has rendered us unable to automatically build and promote images as before. +The process only works for projects under the umbrella. However, the k8s-infra maintainers could manually transfer these images to the new [registry.k8s.io][registry.k8s.io] as a "contingent approach". -See: https://explore.ggcr.dev/?repo=gcr.io%2Fk8s-staging-kubebuilder%2Fkube-rbac-proxy +See: [https://explore.ggcr.dev/?repo=gcr.io%2Fk8s-staging-kubebuilder%2Fkube-rbac-proxy](https://explore.ggcr.dev/?repo=registry.k8s.io%2Fkubebuilder%2Fkube-rbac-proxy) -To continue using kube-rbac-proxy, users will need to update their projects to reference images +To continue using kube-rbac-proxy, users must update their projects to reference images from the new registry. This requires a project update and a new release, ensuring the image references in the `config/default/manager_auth_proxy_patch.yaml` point to a new place. Therefore, the best approach here for those still interested in using -kube-rbac-proxy seems to be to direct them to the images hosted +kube-rbac-proxy seems to direct them to the images hosted at [quay.io](https://quay.io/repository/brancz/kube-rbac-proxy?tab=tags&tag=latest), which are maintained by the project itself and then, we keep those images in the registry.k8s.io as a "contingent approach". @@ -300,7 +300,7 @@ Kubebuilder is not reliable or achievable for Kubebuilder maintainers. It is def Kubebuilder hasn't received any official notice regarding a shutdown of its project there so far, but there's a proactive move to transition away from Google Cloud Platform services due to factors beyond our control. Open communication with our community is key as we explore alternatives. It's important to note the [Container Registry Deprecation][container-registry-dep] results -in users no longer able to consume those images from the current location from **April 22, 2025**, +in users no longer able to consume those images from the current location from **early 2025**, emphasizing the need to shift away from dependent images as soon as possible and communicate it extensively through mailing lists and other channels to ensure community awareness and readiness. @@ -313,35 +313,35 @@ The k8s-infra maintainers assist in ensuring these images will not be lost by: An available option would be to communicate to users to: - a) Replace their registry from `gcr.io/k8s-staging-kubebuilder/kube-rbac-proxy` to `registry.k8s.io/kubebuilder/kube-rbac-proxy` -- b) Clearly state in the docs, Kubebuilder scaffolds, and all channels including email communications that kube-rbac-proxy is in the process of becoming part of Kubernetes/auth-sig but is not yet there and hence is a "not supported/secure" solution +- b) Clearly state in the docs, Kubebuilder scaffolds, and all channels, including email communications, that kube-rbac-proxy is in the process of becoming part of Kubernetes/auth-sig but is not yet there and hence is a "not supported/secure" solution **Cons:** - Kubebuilder would still not be fully compliant with its goals since it would be scaffolding a third-party integration instead of properly endorsing and promoting the usage of external-plugin APIs. - Kubebuilder would still be promoting a solution not deemed secure/safe according to the review by auth-sig maintainers. -- We would still need to manually request k8s-infra maintainers to manually build and promote these images in the new registry. +- We would still need to manually request k8s-infra maintainers to build and promote these images in the new registry manually. - Changes in the manager/project solution delivered in the scaffold have a critical impact. For example, in this case, users -will need to change **ALL** projects supported by them and ensure that their users no longer use their previously released versions. +will need to change **ALL** projects they support and ensure that their users no longer use their previously released versions. Following this path, when kube-rbac-proxy is accepted under the Kubernetes/auth-sig, they will start to maintain and manage -their own images, which means this path will change again and Kubebuilder maintainers have no control over ensuring that +their own images, which means this path will change again, and Kubebuilder maintainers have no control over ensuring that these images will still be available and promoted for a long period. -### Retain kube-rbac-proxy as an Opt-in Feature and move it for an alpha plugin (Unsupported Feature) AND/OR use the project registry +### Retain kube-rbac-proxy as an Opt-in Feature and move it to an alpha plugin (Unsupported Feature) AND/OR use the project registry This alternative keeps kube-rbac-proxy out of the default scaffolds, offering it as an optional plugin for users who choose to integrate it. Clear communication will be crucial to inform users about the implications of using kube-rbac-proxy. **Cons:** -Mainly all cons added for the above alternative option `Replace the current images gcr.io/kubebuilder/kube-rbac-proxy` +Mainly, all cons added for the above alternative option `Replace the current images gcr.io/kubebuilder/kube-rbac-proxy` with `registry.k8s.io/kubebuilder/kube-rbac-proxy` within the exception that we would make clear that we kubebuilder -is unable to manage those images and move the current implementation for alpha plugin -it would maybe make the process to move it from Kubebuilder repository to `kube-rbac-proxy` an -easier process to allow them work with the external plugin. +is unable to manage those images and move the current implementation for the alpha plugin +it would maybe make the process to move it from the Kubebuilder repository to `kube-rbac-proxy` an +easier process to allow them to work with the external plugin. -However, that seems to be a double effort for users and kubebuilder maintainers to deal withing breaking changes -resulting of the ultimate go be achieved. Therefore, it would make more sense -encourage the usage of external-plugins API and add this option in their -repo by once then create this intermediate steps. +However, that is a double effort for users and Kubebuilder maintainers to deal with breaking changes +resulting from achieving the ultimate go. Therefore, it would make more sense +to encourage using external-plugins API and add this option in their +repo once, then create these intermediate steps. [kube-rbac-proxy]: https://github.com/brancz/kube-rbac-proxy [external-plugins]: https://kubebuilder.io/plugins/external-plugins @@ -351,4 +351,4 @@ repo by once then create this intermediate steps. [slack-eta-thread]: https://kubernetes.slack.com/archives/CCK68P2Q2/p1712622102206909 [cr-pr]: https://github.com/kubernetes-sigs/controller-runtime/pull/2407 [k8s-doc-networkpolicies]: https://kubernetes.io/docs/concepts/services-networking/network-policies/ -[cert-manager]:https://cert-manager.io/ \ No newline at end of file +[cert-manager]:https://cert-manager.io/ diff --git a/designs/helm-chart-autogenerate-plugin.md b/designs/helm-chart-autogenerate-plugin.md new file mode 100644 index 00000000000..329daa55486 --- /dev/null +++ b/designs/helm-chart-autogenerate-plugin.md @@ -0,0 +1,567 @@ +| Authors | Creation Date | Status | Extra | +|----------------------------------------|---------------|--------------|-------| +| @dashanji,@camilamacedo86,@LCaparelli | Sep, 2023 | Implemented | - | + +# New Plugin to allow project distribution via helm charts + +This proposal aims to introduce an optional mechanism that allows users +to generate a Helm Chart from their Kubebuilder-scaffolded project. This will +enable them to effectively package and distribute their solutions. + +To achieve this goal, we are proposing a new native Kubebuilder +[plugin](https://book.kubebuilder.io/plugins/plugins) (i.e., `helm-chart/v1-alpha`) +which will provide the necessary scaffolds. The plugin will function similarly +to the existing [Grafana Plugin](https://book.kubebuilder.io/plugins/grafana-v1-alpha), generating or regenerating HelmChart files using the init and edit +sub-commands (i.e., `kubebuilder init|edit --plugins helm-chart/v1-alpha`). + +An alternative solution could be to implement an alpha command, +similar to the [helper provided to upgrade projects](https://book.kubebuilder.io/reference/rescaffold) that would +provide the HelmChart under the `dist`directory, similar to what +is done by [helmify](https://github.com/arttor/helmify). + +## Example of Usage + +**To enable the helm-chart generation when a project is initialized** + +> kubebuilder init --plugins=`go/v4,helm/v1-alpha` + +**To enable the helm-chart generation after the project be scaffolded** + +> kubebuilder edit --plugins=`helm/v1-alpha` + +> Note that the HelmChart should be scaffold under the `dist/` directory in both scenarios: +> ```shell +> example-project/ +> dist/ +> chart/ +> ``` + + +**To sync the HelmChart with the latest changes and add the manifests generated** + +> kubebuilder edit --plugins=`helm/v1-alpha` + +The above command will be responsible for ensuring that the Helm Chart is properly updated +with the latest changes in the project, including the files generated by +controller-gen when users run make manifests. + +## Open Questions + +### 1) How to manage and scaffold the CRDs for the HelmChart? + +According to [Helm Best Practices for Custom Resource Definitions](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you), +there are two main methods for handling CRDs: + +- **Method 1:Let Helm Do It For You:** Place CRDs in the `crds/` directory. Helm installs these CRDs during the initial + install but does not manage upgrades or deletions. +- **Method 2:Separate Charts:** Place the CRD definition in one chart + and the resources using the CRD in another chart. + This method requires separate installations for each chart. + +**Raised Considerations and Concerns** +- **Use Helm crd directory** The upgraded chart versions will silently ignore CRDs even if they differ from the installed versions. This could lead to surprising and unexpected behavior. Therefore, Kubebuilder should not encourage or promote this approach. +- **Templates Folder**: Moving CRDs to the `templates` folder facilitates upgrades but uninstalls CRDs when the operator is uninstalled. However, it allows users easier manage the CRDs and install them on upgrades. It is a common approach adopted by maintainers but is not considered a good practice by Helm itself. +- **Separate Helm Chart for CRDs:** This approach allows control over both CRD and operator versions without deleting CRDs when the operator chart is deleted. Also, follows the HelmChart best practices. Another problem with this approach is to ensure that the CRDs will be applied before the CRs since both will be under the template directory. +- **When Webhooks are used:** If a CRD specifies, for example, a conversion webhook, the "API chart" needs to contain the CRDs and the webhook `service/workload`. + It would also make sense to include `validating/mutating` webhooks, requiring the scaffolding of separate main modules and image builds for + webhooks and controllers which does not shows to be compatible with Kubebuilder Golang scaffold. + +**Proposed Solution** + +Follow the same approach adopted by [Cert-Manager](https://cert-manager.io/docs/installation/helm/). +Add the CRDs under the `template` directory and have a spec in the `values.yaml` +which will define if the CRDs should or not be applied + +```shell +helm install|upgrade \ + myrelease \ + --namespace my-namespace \ + --set `crds.enabled=true` +``` + +Also, add another spec to the `values.yaml` to not allow the CRDs +be deleted when the helm is uninstalled: + +```yaml + # START annotations {{- if .Values.crds.keep }} + annotations: + helm.sh/resource-policy: keep + # END annotations {{- end }} +``` + +Additionally, we might want to +scaffold separate charts for the APIs and support both. +An example of this approach provided as feedback +was [karpenter-provider-aws](https://github.com/aws/karpenter-provider-aws/tree/main/charts). + +We should either make clear the usage of both supported +ways and clarify their limitations. However, the proposed +solution would result in the following layout: + +``` +example-project/ + dist/ + chart/ + example-project-crd/ + ├── Chart.yaml + ├── templates/ + │ ├── _helpers.tpl + │ ├── crds/ + │ │ └── + └── values.yaml + example-project/ + ├── Chart.yaml + ├── templates/ + │ ├── _helpers.tpl + │ ├── crds/ + │ └── + │ ├── ... +``` + +### 2) How to manage dependencies such as Cert-Manager and Prometheus? + +Helm charts allow maintainers to define dependencies via the `Chart.yaml` file. +However, in the initial version of this plugin at least, we do not need to consider management of dependencies. + +Adding dependencies such as **Cert-Manager** and **Prometheus** directly in the `Chart.yaml` +could introduce issues since these components are intended to be installed only once per cluster. +Attempting to manage multiple installations could lead to conflicts and cause unintended behaviors, +especially in shared cluster environments. + +To avoid these issues, the plugin for now will not scaffold this file and will not try to +manage it. Instead, users will be responsible for managing these dependencies outside of +the generated Helm chart, ensuring they are correctly installed and only installed once in the cluster. + +## Motivation + +Currently, projects scaffolded with Kubebuilder can be distributed via YAML. Users can run +`make build-installer IMG=/:tag`, which will generate `dist/install.yaml`. +Therefore, its consumers can install the solution by applying this YAML file, such as: +`kubectl apply -f https://raw.githubusercontent.com////dist/install.yaml`. + +However, many adopt solutions require the Helm Chart format, such as FluxCD. Therefore, +maintainers are looking to also provide their solutions via Helm Chart. Users currently face the challenges of lacking +an officially supported distribution mechanism for Helm Charts. They seek to: + +- Harness the power of Helm Chart as a package manager for the project, enabling seamless adaptation to diverse deployment environments. +- Take advantage of Helm's dependency management capabilities to simplify the installation process of project dependencies, such as cert-manager. +- Seamlessly integrate with Helm's ecosystem, including FluxCD, to efficiently manage the project. + +Consequently, this proposal aims to introduce a method that allows Kubebuilder users to easily distribute their projects through Helm Charts, a strategy that many well-known projects have adopted: + +- [mongodb](https://artifacthub.io/packages/helm/mongodb-helm-charts/community-operator) +- [cert-manager](https://cert-manager.io/v1.6-docs/installation/helm/#1-add-the-jetstack-helm-repository) +- [prometheus](https://bitnami.com/stack/prometheus-operator/helm) +- [aws-load-balancer-controller](https://github.com/kubernetes-sigs/aws-load-balancer-controller/tree/main/helm/aws-load-balancer-controller) + +**NOTE:** For further context see the [discussion topic](https://github.com/kubernetes-sigs/kubebuilder/discussions/3074) + +## Goals + +- Allow Kubebuilder users distribute their projects using Helm easily. +- Make the best effort to preserve any customizations made to the Helm Charts by the users, which means we will skip syncs in the `values.ymal`. +- Stick with Helm layout definitions and externalize into the relevant values-only options to distribute the default scaffold done by Kubebuilder. We should follow https://helm.sh/docs/chart_best_practices. + +## Non-Goals + +- Converting any Kustomize configuration to Helm Charts like [helmify](https://github.com/arttor/helmify) does. +- Support the deprecated plugins. This option should be supported from `go/v4` and `kustomize/v2` +- Introduce support for Helm in addition to Kustomize, or replace Kustomize with Helm entirely, similar to the approach +taken by Operator-SDK, thereby allowing users to utilize Helm Charts to build their Project. +- Attend standard practices that deviate from Helm Chart layout, definition, or conventions to workaround its limitations. + +## User Stories + +- As a developer, I want to be able to generate a helm chart from a kustomize directory so that I can distribute the helm chart to my users. Also, I want the + generation to be as simple as possible without the need to write any additional duplicate files. +- As a user, I want the helm chart can cover all potential configurations when I deploy it on the Kubernetes cluster. +- As a platform engineer, I want to be able to manage different versions and configurations of a project across multiple clusters and environments based on the same distribution artifact (Helm Chart), with versioning and dependency locking for supply chain security. + +## Implementation Details/Notes/Constraints + +### Plugin Layout + +- **Location and Versioning**: The new plugin should follow Kubebuilder standards and +be implemented under `pkg/plugins/optional`. It should be introduced as an alpha version +(`v1alpha`), similar to the [Grafana plugin](https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/plugins/optional/grafana/v1alpha). + +- **The data should be tracked in PROJECT File**: Usage of the plugin should be tracked in the `PROJECT` +file with the input via flags and options if required. Example entry in the `PROJECT` file: + +```yaml +... +plugins: + helm.go.kubebuilder.io/v1-alpha: + options: ## (If ANY) + : +``` + +Ensure that user-provided input is properly tracked, similar to how it's done in +other plugins [(see the code in the plugin.go)](https://github.com/kubernetes-sigs/kubebuilder/blob/c058fb95fe0ccd8d2a3147990251ca501df5eb26/pkg/plugins/golang/deploy-image/v1alpha1/plugin.go#L58-L75) +and the [(code source to track the data)](https://github.com/kubernetes-sigs/kubebuilder/blob/c058fb95fe0ccd8d2a3147990251ca501df5eb26/pkg/plugins/golang/deploy-image/v1alpha1/api.go#L191-L217) +of the deploy-image plugin for reference. + +**NOTE** We might not need options/flags in the first implementation. However, we should still track the plugin as +we do for the Grafana plugin. + +### Plugin Implementation Structure + +Following the structure implementation for the source code of this plugin: + +```shell +. +├── helm-chart +│ └── v1alpha1 +│ ├── init.go +│ ├── edit.go +│ ├── plugin.go +│ └── scaffolds +│ ├── init.go +│ ├── edit.go +│ └── internal +│ └── templates +``` + +### SubCommand implementation + +For each subCommand we will need to check the resources which +are scaffold for each subCommand via the [kustomize](https://kubebuilder.io/plugins/kustomize-v2) plugin +and ensure that we will implement the subCommand of the HelmChart plugin +to the respective scaffolds as well. + +#### To Sync the Manifests Created with `controller-gen` + +Users will need to call the subcommand `edit` passing the plugin to +ensure that the Helm chart is properly synced. + +Therefore, the `PostScaffold` of this command could perform steps such as: + +- **Run `make manifests`**: Generate the latest CRDs and other manifests. +- **Copy the files to Helm chart templates**: + - Copy CRDs: `cp config/crd/bases/*.yaml chart/example-project-crd/templates/crds/` + - Copy RBAC manifests: `cp config/rbac/*.yaml chart/example-project/templates/rbac/` + - Copy webhook configurations: `cp config/webhook/*.yaml chart/example-project/templates/webhook/` + - Copy the manager manifest: `cp config/default/manager.yaml chart/example-project/templates/manager/manager.ymal0` +- **Replace placeholders with Helm values**: Ensure that customized fields, such as the namespace, are properly replaced accordingly. + Example: Replace `name: system` with `{{ .Values.Release.name }}`. + +This ensures the Helm chart is always up-to-date with the latest +manifests generated by Kubebuilder, maintaining consistency with the +configured namespace and other customizable fields. + +We will need to use the utils helpers such as [ReplaceInFile](https://github.com/kubernetes-sigs/kubebuilder/blob/c058fb95fe0ccd8d2a3147990251ca501df5eb26/pkg/plugin/util/util.go#L303-L323) +or [EnsureExistAndReplace](https://github.com/kubernetes-sigs/kubebuilder/blob/c058fb95fe0ccd8d2a3147990251ca501df5eb26/pkg/plugin/util/util.go#L276) +to achieve this goal. + +### HelmChart Values Scaffolded by the Plugin + +- **Allow values.yaml to be fully re-generated with the flag --force**: + +By default, the `values.yaml` file should not +be overwritten. However, users should have the option to overwrite it using +a flag (`--force=true`). + +This can be implemented in the specific template as done for other plugins: + +```go +if f.Force { + f.IfExistsAction = machinery.OverwriteFile +} else { + f.IfExistsAction = machinery.Error +} +``` + +**NOTE:** We will evaluate the cases when we implement `webhook.go` and `api.go` +for the HelmChart plugin. However, we might use the force flag to replicate +the same behavior implemented in the subCommands of the kustomize plugin. +For instance, if the flag is used when creating an API, it forces +the overwriten of the generated samples. Similarly, if the api subCommand +of the HelmChart plugin is called with `--force`, we should replace +all samples with the latest versions instead of only adding the new one. + +- **Helm Chart Templates should have conditions**: + +Ensure templates install resources based on +conditions defined in the `values.yaml`. Example for CRDs: + +``` +# To install CRDs +{{- if .Values.crd.enable }} +... +{{- end }} +``` + +- **Customizable Values**: Set customizable values in the `values.yaml`, + such as defining ServiceAccount names, and whether they should be created or not. + Furthermore, we should include comments to help end-users understand the source + of configurations. Example: + +```yaml +{{- if .Values.rbac.enable }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: project-v4 + app.kubernetes.io/managed-by: kustomize + name: {{ .Values.rbac.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end }} +``` + +- **Example of values.yaml**: + +Following an example to illustrate the expected result of this plugin: + +```yaml +# Install CRDs under the template +crd: + enable: false + keep: true + +# Webhook configuration sourced from the `config/webhook` +webhook: + enabled: true + conversion: + enabled: true + +## RBAC configuration under the `config/rbac` directory +rbac: + create: true + serviceAccountName: "controller-manager" + +# Cert-manager configuration +certmanager: + enabled: false + issuerName: "letsencrypt-prod" + commonName: "example.com" + dnsName: "example.com" + +# Network policy configuration sourced from the `config/network_policy` +networkPolicy: + enabled: false + +# Prometheus configuration +prometheus: + enabled: false + +# Manager configuration sourced from the `config/manager` +manager: + replicas: 1 + image: + repository: "controller" + tag: "latest" + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 64Mi + +# Metrics configuration sourced from the `config/metrics` +metrics: + enabled: true + +# Leader election configuration sourced from the `config/leader_election` +leaderElection: + enabled: true + role: "leader-election-role" + rolebinding: "leader-election-rolebinding" + + +# Controller Manager configuration sourced from the `config/manager` +controllerManager: + manager: + args: + - --metrics-bind-address=:8443 + - --leader-elect + - --health-probe-bind-address=:8081 + containerSecurityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + image: + repository: controller + tag: latest + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + replicas: 1 + serviceAccount: + annotations: {} + +# Kubernetes cluster domain configuration +kubernetesClusterDomain: cluster.local + +# Metrics service configuration sourced from the `config/metrics` +metricsService: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + type: ClusterIP + +# Webhook service configuration sourced from the `config/webhook` +webhookService: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + type: ClusterIP +``` + +### Optional configurations should be disabled by default + +The HelmChart plugin should not scaffold optional options enabled +when those are scaffolded as disabled by the default implementation +of `kustomize/v2` and consequently the `go/v4` plugin used by default. Example: + +The dependency on Cert-Manager is disabled by default. + +```yaml +From config/default/kusyomization.yaml +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +``` + +Therefore, by default the `values.yaml` should be scaffolded with: + +``` +# Cert-manager configuration +certmanager: + enabled: false +``` + +### Layout of the Helm-Chart + +Following an example of the expected result of this plugin: + +```shell +example-project/ + dist/ + chart/ + example-project-crd/ + ├── Chart.yaml + ├── templates/ + │ ├── _helpers.tpl + │ ├── crds/ + │ │ └── + └── values.yaml + example-project/ + ├── Chart.yaml + ├── templates/ + │ ├── _helpers.tpl + │ ├── crds/ + │ └── + │ ├── certmanager/ + │ │ └── certificate.yaml + │ ├── manager/ + │ │ └── manager.yaml + │ ├── network-policy/ + │ │ ├── allow-metrics-traffic.yaml + │ │ └── allow-webhook-traffic.yaml // Should be added by the plugin subCommand webhook.go + │ ├── prometheus/ + │ │ └── monitor.yaml + │ ├── rbac/ + │ │ ├── kind_editor_role.yaml + │ │ ├── kind_viewer_role.yaml + │ │ ├── leader_election_role.yaml + │ │ ├── leader_election_role_binding.yaml + │ │ ├── metrics_auth_role.yaml + │ │ ├── metrics_auth_role_binding.yaml + │ │ ├── metrics_reader_role.yaml + │ │ ├── role.yaml + │ │ ├── role_binding.yaml + │ │ └── service_account.yaml + │ ├── samples/ + │ │ └── kind_version_admiral.yaml + │ ├── webhook/ + │ │ ├── manifests.yaml + │ │ └── service.yaml + └── values.yaml +``` + +### Update the README + +A README.md is scaffold for the projects. (see its implementation [here](https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/golang/v4/scaffolds/internal/templates/readme.go)). +Therefore, if the project is scaffold with the HelmChart plugin then, +we should update the [Distribution](https://github.com/kubernetes-sigs/kubebuilder/blob/master/testdata/project-v4/README.md#project-distribution) +section to add the info and steps over how to keep the HelmChart synced. + +### Tests and samples + +To ensure that the new plugin will work well we will need to: +- Implement e2e tests for the plugin. (for reference see the e2e tests for the [DeployImage](https://github.com/kubernetes-sigs/kubebuilder/tree/master/test/e2e/deployimage)) +- Ensure that the plugin is scaffold with all samples under the [testdata](https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata) directory (we will need call the plugin in [test/testdata/generate.sh](https://github.com/kubernetes-sigs/kubebuilder/blob/master/test/testdata/generate.sh#L115-L119)) + +### Documentation + +The new plugin should either be properly documented such as the others. +For reference see: +- [Available Plugins](https://book.kubebuilder.io/plugins/available-plugins) +- [DeployImage Plugin](https://book.kubebuilder.io/plugins/deploy-image-plugin-v1-alpha) + +## Risks and Mitigations + +**Difficulty in Maintaining the Solution** + +Maintaining the solution may prove challenging in the long term, +particularly if it does not gain community adoption and, consequently, collaboration. +To mitigate this risk, the proposal aims to introduce an optional alpha plugin or to +implement it through an alpha command. This approach provides us with greater flexibility +to make adjustments or, if necessary, to deprecate the feature without definitively +compromising support. + +## Proof of Concept + +In order to prove that would be possible we could +refer to the open source tool +[helmify](https://github.com/arttor/helmify). + +## Drawbacks + +**Inability to Handle Complex Kubebuilder Scenarios** + +The proposed plugin may struggle to appropriately handle complex scenarios commonly encountered +in Kubebuilder projects, such as intricate webhook configurations. Kubebuilder’s scaffolded +projects can have sophisticated webhook setups, and translating these accurately into Helm +Charts may prove challenging. This could result in Helm Charts that are not fully reflective +of the original project’s functionality or configurations. + +**Incomplete Generation of Valid and Deployable Helm Charts** + +The proposed solution may not be capable of generating a fully valid and deployable Helm Chart +for all use cases supported by Kubebuilder. Given the diversity and complexity of potential +configurations within Kubebuilder projects, there is a risk that the generated Helm Charts +may require significant manual intervention to be functional. This drawback undermines the +goal of simplifying distribution via Helm Charts and could lead to frustration for users who +expect a seamless and automated process. + +## Alternatives + +**Via a new command (Alternative Option)** + +By running the following command, the plugin will generate a helm chart from the specific kustomize directory and output it to the directory specified by the `--output` flag. + +```shell +kubebuilder alpha generate-helm-chart --from= --output= +``` + +The main drawback of this option is that it does not adhere to the Kubebuilder ecosystem. +Additionally, we would not take advantage of Kubebuilder library features, such as avoiding +overwriting the `values.yaml`. It might also be harder to support and maintain since we would +not have the templates as we usually do. + +Lastly, another con is that it would not allow us to scaffold projects with the plugin +enabled and in the future provide further configurations and customizations for this plugin. +These configurations would be tracked in the `PROJECT` file, allowing integration with other +projects, extensions, and the re-scaffolding of the HelmChart while preserving the inputs +provided by the user via plugins flags as it is done for example for +the [Deploy Image](https://book.kubebuilder.io/plugins/deploy-image-plugin-v1-alpha) plugin. diff --git a/docs/CONTRIBUTING-ROLES.md b/docs/CONTRIBUTING-ROLES.md index d7536b03924..446a3af2e52 100644 --- a/docs/CONTRIBUTING-ROLES.md +++ b/docs/CONTRIBUTING-ROLES.md @@ -33,7 +33,7 @@ sponsor you, just ping us on Slack :-)** ## Reviewers -Reviewers are recongized as able to provide code reviews for parts of the +Reviewers are recognized as able to provide code reviews for parts of the codebase, and are entered into the `reviewers` section of one or more `OWNERS` files. You'll get auto-assigned reviews for your area of the codebase, and are generally expected to review for both correctness, @@ -86,7 +86,7 @@ Things to look for: - Does it expose a new type from `k8s.io/XYZ`, and, if so, is it worth it? Is that piece well-designed? -**For large changes, approvers are responsible for getting reasonble +**For large changes, approvers are responsible for getting reasonable consensus**. With the power to approve such changes comes the responsibility of ensuring that the project as a whole has time to discuss them. diff --git a/docs/book/book.toml b/docs/book/book.toml index 007406d3d85..2b6697936c6 100644 --- a/docs/book/book.toml +++ b/docs/book/book.toml @@ -17,8 +17,8 @@ command = "./litgo.sh" command = "./markerdocs.sh" [context.environment] - environment = { GO_VERSION = "1.22" } + environment = { GO_VERSION = "1.23" } [context.deploy-preview.environment] - environment = { GO_VERSION = "1.22" } + environment = { GO_VERSION = "1.23" } diff --git a/docs/book/install-and-build.sh b/docs/book/install-and-build.sh index a7f7bbf6ec4..48d086332db 100755 --- a/docs/book/install-and-build.sh +++ b/docs/book/install-and-build.sh @@ -71,7 +71,7 @@ chmod +x /tmp/mdbook echo "grabbing the latest released controller-gen" go version -go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.4 +go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.17.0 # make sure we add the go bin directory to our path gobin=$(go env GOBIN) diff --git a/docs/book/src/cronjob-tutorial/testdata/emptymain.go b/docs/book/src/cronjob-tutorial/testdata/emptymain.go index 0b67e1b9de0..5bcdf964549 100644 --- a/docs/book/src/cronjob-tutorial/testdata/emptymain.go +++ b/docs/book/src/cronjob-tutorial/testdata/emptymain.go @@ -143,7 +143,7 @@ func main() { The above example will change the scope of your project to a single `Namespace`. In this scenario, it is also suggested to restrict the provided authorization to this namespace by replacing the default `ClusterRole` and `ClusterRoleBinding` to `Role` and `RoleBinding` respectively. - For further information see the Kubernetes documentation about Using [RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). + For further information see the Kubernetes documentation about [Using RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). Also, it is possible to use the [`DefaultNamespaces`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/cache#Options) from `cache.Options{}` to cache objects in a specific set of namespaces: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json b/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json index e2cdc09c96c..259f0f67268 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json +++ b/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Kubebuilder DevContainer", - "image": "golang:1.22", + "image": "golang:1.23", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/lint.yml b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/lint.yml index f40d36579ce..46511063a49 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/lint.yml +++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/lint.yml @@ -15,9 +15,9 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Run linter uses: golangci/golangci-lint-action@v6 with: - version: v1.61 + version: v1.62.2 diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-e2e.yml b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-e2e.yml index 8780644002a..b2eda8c3db0 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-e2e.yml +++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-e2e.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test.yml b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test.yml index 7baf6579399..fc2e80d304d 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test.yml +++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Running Tests run: | diff --git a/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile b/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile index 4ba18b68cc4..5c73c7f37cd 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile +++ b/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/docs/book/src/cronjob-tutorial/testdata/project/Makefile b/docs/book/src/cronjob-tutorial/testdata/project/Makefile index ba36d148420..230d92c319b 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/Makefile +++ b/docs/book/src/cronjob-tutorial/testdata/project/Makefile @@ -1,7 +1,5 @@ # Image URL to use all building/pushing image targets IMG ?= controller:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.31.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -64,7 +62,7 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. +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'. @@ -92,6 +90,10 @@ lint: golangci-lint ## Run golangci-lint linter 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 + ##@ Build .PHONY: build @@ -175,9 +177,12 @@ GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions KUSTOMIZE_VERSION ?= v5.5.0 -CONTROLLER_TOOLS_VERSION ?= v0.16.4 -ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.61.0 +CONTROLLER_TOOLS_VERSION ?= v0.17.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", $$3}') +GOLANGCI_LINT_VERSION ?= v1.62.2 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. @@ -189,6 +194,14 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(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; \ + } + .PHONY: envtest envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) diff --git a/docs/book/src/cronjob-tutorial/testdata/project/README.md b/docs/book/src/cronjob-tutorial/testdata/project/README.md index b5295ca3fa8..1d6e7bcd560 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/README.md +++ b/docs/book/src/cronjob-tutorial/testdata/project/README.md @@ -7,7 +7,7 @@ ## Getting Started ### Prerequisites -- go version v1.22.0+ +- go version v1.23.0+ - docker version 17.03+. - kubectl version v1.11.3+. - Access to a Kubernetes v1.11.3+ cluster. @@ -68,7 +68,9 @@ make undeploy ## Project Distribution -Following are the steps to build the installer and distribute this project to users. +Following the options to release and provide this solution to the users. + +### By providing a bundle with all YAML files 1. Build the installer for the image built and published in the registry: @@ -76,19 +78,38 @@ Following are the steps to build the installer and distribute this project to us make build-installer IMG=/project:tag ``` -NOTE: The makefile target mentioned above generates an 'install.yaml' +**NOTE:** The makefile target mentioned above generates an 'install.yaml' file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without -its dependencies. +with Kustomize, which are necessary to install this project without its +dependencies. 2. Using the installer -Users can just run kubectl apply -f to install the project, i.e.: +Users can just run 'kubectl apply -f ' to install +the project, i.e.: ```sh kubectl apply -f https://raw.githubusercontent.com//project//dist/install.yaml ``` +### By providing a Helm Chart + +1. Build the chart using the optional helm plugin + +```sh +kubebuilder edit --plugins=helm/v1-alpha +``` + +2. See that a chart was generated under 'dist/chart', and users +can obtain this solution from there. + +**NOTE:** If you change the project, you need to update the Helm Chart +using the same command above to sync the latest changes. Furthermore, +if you create webhooks, you need to use the above command with +the '--force' flag and manually ensure that any custom configuration +previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' +is manually re-applied afterwards. + ## Contributing // TODO(user): Add detailed information on how you would like others to contribute to this project @@ -98,7 +119,7 @@ More information can be found via the [Kubebuilder Documentation](https://book.k ## License -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_types.go b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_types.go index ee782bfb488..da4b0747931 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_types.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_types.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/groupversion_info.go b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/groupversion_info.go index 0c46f1df6ce..b1fcbf8fed5 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/groupversion_info.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go index 5f32c3ab478..740762cd3ce 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go b/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go index fa0d01fbbde..64ce6e102b4 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import ( "crypto/tls" "flag" "os" + "path/filepath" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -30,6 +31,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -70,10 +72,13 @@ The other thing that's changed is that kubebuilder has added a block calling our CronJob controller's `SetupWithManager` method. */ +// nolint:gocyclo func main() { /* */ var metricsAddr string + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string var enableLeaderElection bool var probeAddr string var secureMetrics bool @@ -87,6 +92,13 @@ func main() { "Enabling this will ensure there is only one active controller manager.") flag.BoolVar(&secureMetrics, "metrics-secure", true, "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") opts := zap.Options{ @@ -112,13 +124,38 @@ func main() { tlsOpts = append(tlsOpts, disableHTTP2) } + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + webhookServer := webhook.NewServer(webhook.Options{ - TLSOpts: tlsOpts, + TLSOpts: webhookTLSOpts, }) // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. // More info: - // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/metrics/server // - https://book.kubebuilder.io/reference/metrics.html metricsServerOptions := metricsserver.Options{ BindAddress: metricsAddr, @@ -130,12 +167,35 @@ func main() { // FilterProvider is used to protect the metrics endpoint with authn/authz. // These configurations ensure that only authorized users and service accounts // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: - // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/metrics/filters#WithAuthenticationAndAuthorization metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } - // TODO(user): If CertDir, CertName, and KeyName are not specified, controller-runtime will automatically - // generate self-signed certificates for the metrics server. While convenient for development and testing, - // this setup is not recommended for production. + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "to initialize metrics certificate watcher", "error", err) + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ @@ -189,6 +249,22 @@ func main() { } // +kubebuilder:scaffold:builder + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1) diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-metrics.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-metrics.yaml new file mode 100644 index 00000000000..f05703fa73e --- /dev/null +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-metrics.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a metrics certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + dnsNames: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml similarity index 50% rename from docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate.yaml rename to docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml index b51082a01e6..ae025c9c6ed 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml @@ -1,35 +1,20 @@ # The following manifests contain a self-signed issuer CR and a certificate CR. # More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - labels: - app.kubernetes.io/name: project - app.kubernetes.io/managed-by: kustomize - name: selfsigned-issuer - namespace: system -spec: - selfSigned: {} ---- apiVersion: cert-manager.io/v1 kind: Certificate metadata: labels: - app.kubernetes.io/name: certificate - app.kubernetes.io/instance: serving-cert - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: project - app.kubernetes.io/part-of: project + app.kubernetes.io/name: project app.kubernetes.io/managed-by: kustomize name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml namespace: system spec: # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. dnsNames: - SERVICE_NAME.SERVICE_NAMESPACE.svc - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local issuerRef: kind: Issuer name: selfsigned-issuer - secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize + secretName: webhook-server-cert diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/issuer.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/issuer.yaml new file mode 100644 index 00000000000..1c600ce5a67 --- /dev/null +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/issuer.yaml @@ -0,0 +1,13 @@ +# The following manifest contains a self-signed issuer CR. +# More information can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomization.yaml index bebea5a595e..fcb7498e468 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomization.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomization.yaml @@ -1,5 +1,7 @@ resources: -- certificate.yaml +- issuer.yaml +- certificate-webhook.yaml +- certificate-metrics.yaml configurations: - kustomizeconfig.yaml diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml index e572bd64a30..215f231b20f 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.17.0 name: cronjobs.batch.tutorial.kubebuilder.io spec: group: batch.tutorial.kubebuilder.io diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/crd/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/crd/kustomization.yaml index ce4e7415d87..398aa123164 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/crd/kustomization.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/crd/kustomization.yaml @@ -10,10 +10,6 @@ patches: # patches here are for enabling the conversion webhook for each CRD # +kubebuilder:scaffold:crdkustomizewebhookpatch -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -# +kubebuilder:scaffold:crdkustomizecainjectionpatch - # [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. #configurations: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/cert_metrics_manager_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/cert_metrics_manager_patch.yaml new file mode 100644 index 00000000000..d975015538e --- /dev/null +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/default/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/kustomization.yaml index 8778c1a5150..3e54d96aea9 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/default/kustomization.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/default/kustomization.yaml @@ -33,7 +33,7 @@ resources: # be able to communicate with the Webhook Server. #- ../network-policy -# Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager +# Uncomment the patches line if you enable Metrics patches: # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. # More info: https://book.kubebuilder.io/reference/metrics @@ -41,13 +41,60 @@ patches: target: kind: Deployment +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +- path: cert_metrics_manager_patch.yaml + target: + kind: Deployment + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml - path: manager_webhook_patch.yaml + target: + kind: Deployment # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations replacements: + - source: # Uncomment the following block to enable certificates for metrics + kind: Service + version: v1 + name: controller-manager-metrics-service + fieldPath: metadata.name + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + name: metrics-certs + fieldPaths: + - spec.dnsNames.0 + - spec.dnsNames.1 + options: + delimiter: '.' + index: 0 + create: true + + - source: + kind: Service + version: v1 + name: controller-manager-metrics-service + fieldPath: metadata.namespace + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + name: metrics-certs + fieldPaths: + - spec.dnsNames.0 + - spec.dnsNames.1 + options: + delimiter: '.' + index: 1 + create: true + - source: # Uncomment the following block if you have any webhook kind: Service version: v1 @@ -58,6 +105,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 + name: serving-cert fieldPaths: - .spec.dnsNames.0 - .spec.dnsNames.1 @@ -75,6 +123,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 + name: serving-cert fieldPaths: - .spec.dnsNames.0 - .spec.dnsNames.1 @@ -102,7 +151,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.name targets: - select: @@ -118,7 +167,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.namespace # Namespace of the certificate CR targets: - select: @@ -133,7 +182,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.name targets: - select: @@ -149,29 +198,15 @@ replacements: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns # - source: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.name -# targets: -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_webhook_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_webhook_patch.yaml index 06ab33e4e87..963c8a4cc63 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_webhook_patch.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_webhook_patch.yaml @@ -1,26 +1,31 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system - labels: - app.kubernetes.io/name: project - app.kubernetes.io/managed-by: kustomize -spec: - template: - spec: - containers: - - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert +# This patch ensures the webhook certificates are properly mounted in the manager container. +# It configures the necessary arguments, volumes, volume mounts, and container ports. + +# Add the --webhook-cert-path argument for configuring the webhook certificate path +- op: add + path: /spec/template/spec/containers/0/args/- + value: --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs + +# Add the volumeMount for the webhook certificates +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + +# Add the port configuration for the webhook server +- op: add + path: /spec/template/spec/containers/0/ports/- + value: + containerPort: 9443 + name: webhook-server + protocol: TCP + +# Add the volume configuration for the webhook certificates +- op: add + path: /spec/template/spec/volumes/- + value: + name: webhook-certs + secret: + secretName: webhook-server-cert diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/metrics_service.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/metrics_service.yaml index 4425b9b8977..df85a7387c5 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/default/metrics_service.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/default/metrics_service.yaml @@ -15,3 +15,4 @@ spec: targetPort: 8443 selector: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/manager/manager.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/manager/manager.yaml index 1bb9d5a6485..989f80ca5fe 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/manager/manager.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/manager/manager.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project replicas: 1 template: metadata: @@ -27,6 +28,7 @@ spec: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager + app.kubernetes.io/name: project spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -49,14 +51,12 @@ spec: # values: # - linux securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - command: - /manager @@ -65,6 +65,7 @@ spec: - --health-probe-bind-address=:8081 image: controller:latest name: manager + ports: [] securityContext: allowPrivilegeEscalation: false capabilities: @@ -91,5 +92,7 @@ spec: requests: cpu: 10m memory: 64Mi + volumeMounts: [] + volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml index de6ec5f8097..0e64d74ef6e 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml @@ -1,6 +1,6 @@ # This NetworkPolicy allows ingress traffic # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those -# namespaces are able to gathering data from the metrics endpoint. +# namespaces are able to gather data from the metrics endpoint. apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: @@ -13,6 +13,7 @@ spec: podSelector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project policyTypes: - Ingress ingress: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml index 4de86e58119..eb9464b64ea 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml @@ -13,6 +13,7 @@ spec: podSelector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project policyTypes: - Ingress ingress: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/kustomization.yaml index ed137168a1d..8126ea89b1a 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/kustomization.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/kustomization.yaml @@ -1,2 +1,11 @@ resources: - monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +patches: + - path: monitor_tls_patch.yaml + target: + kind: ServiceMonitor diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor.yaml index 1dea5d5fd7b..3a0798c331e 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor.yaml @@ -16,15 +16,12 @@ spec: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables - # certificate verification. This poses a significant security risk by making the system vulnerable to - # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between - # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, - # compromising the integrity and confidentiality of the information. - # Please use the following options for secure configurations: - # caFile: /etc/metrics-certs/ca.crt - # certFile: /etc/metrics-certs/tls.crt - # keyFile: /etc/metrics-certs/tls.key + # certificate verification, exposing the system to potential man-in-the-middle attacks. + # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. + # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, + # which securely references the certificate from the 'metrics-server-cert' secret. insecureSkipVerify: true selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml new file mode 100644 index 00000000000..e824dd0ff86 --- /dev/null +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml @@ -0,0 +1,22 @@ +# Patch for Prometheus ServiceMonitor to enable secure TLS configuration +# using certificates managed by cert-manager +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - tlsConfig: + 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 diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_admin_role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_admin_role.yaml new file mode 100644 index 00000000000..234d656da08 --- /dev/null +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project project itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over batch.tutorial.kubebuilder.io. +# 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: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: cronjob-admin-role +rules: +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs + verbs: + - '*' +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs/status + verbs: + - get diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml index c36d86e55d6..f0ccbbe8662 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to edit cronjobs. +# This rule is not used by the project project 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 batch.tutorial.kubebuilder.io. +# 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: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml index 0bfb9809718..d8200790df3 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to view cronjobs. +# This rule is not used by the project project itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to batch.tutorial.kubebuilder.io 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: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/kustomization.yaml index 39fe987357a..53466ccd0ac 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/kustomization.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/kustomization.yaml @@ -18,10 +18,11 @@ resources: - metrics_auth_role.yaml - metrics_auth_role_binding.yaml - metrics_reader_role.yaml -# For each CRD, "Editor" and "Viewer" roles are scaffolded by +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by # default, aiding admins in cluster management. Those roles are -# not used by the Project itself. You can comment the following lines +# not used by the {{ .ProjectName }} itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- cronjob_admin_role.yaml - cronjob_editor_role.yaml - cronjob_viewer_role.yaml diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/service.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/service.yaml index 1c94b010702..34c7252616d 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/service.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/service.yaml @@ -13,3 +13,4 @@ spec: targetPort: 9443 selector: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml index 2cb67e5e885..85c9c847db0 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml +++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml @@ -11,7 +11,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.17.0 name: cronjobs.batch.tutorial.kubebuilder.io spec: group: batch.tutorial.kubebuilder.io @@ -3865,6 +3865,27 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + name: project-cronjob-admin-role +rules: +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs + verbs: + - '*' +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole metadata: labels: app.kubernetes.io/managed-by: kustomize @@ -4053,6 +4074,7 @@ spec: protocol: TCP targetPort: 8443 selector: + app.kubernetes.io/name: project control-plane: controller-manager --- apiVersion: v1 @@ -4069,6 +4091,7 @@ spec: protocol: TCP targetPort: 9443 selector: + app.kubernetes.io/name: project control-plane: controller-manager --- apiVersion: apps/v1 @@ -4084,12 +4107,14 @@ spec: replicas: 1 selector: matchLabels: + app.kubernetes.io/name: project control-plane: controller-manager template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: + app.kubernetes.io/name: project control-plane: controller-manager spec: containers: @@ -4097,6 +4122,8 @@ spec: - --metrics-bind-address=:8443 - --leader-elect - --health-probe-bind-address=:8081 + - --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + - --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs command: - /manager image: controller:latest @@ -4130,22 +4157,113 @@ spec: drop: - ALL volumeMounts: + - mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert + name: webhook-certs readOnly: true securityContext: runAsNonRoot: true + seccompProfile: + type: RuntimeDefault serviceAccountName: project-controller-manager terminationGracePeriodSeconds: 10 volumes: - - name: cert + - name: metrics-certs + secret: + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + optional: false + secretName: metrics-server-cert + - name: webhook-certs secret: - defaultMode: 420 secretName: webhook-server-cert --- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + name: project-metrics-certs + namespace: project-system +spec: + dnsNames: + - project-controller-manager-metrics-service.project-system.svc + - project-controller-manager-metrics-service.project-system.svc.cluster.local + issuerRef: + kind: Issuer + name: project-selfsigned-issuer + secretName: metrics-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + name: project-serving-cert + namespace: project-system +spec: + dnsNames: + - project-webhook-service.project-system.svc + - project-webhook-service.project-system.svc.cluster.local + issuerRef: + kind: Issuer + name: project-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + name: project-selfsigned-issuer + namespace: project-system +spec: + selfSigned: {} +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + control-plane: controller-manager + name: project-controller-manager-metrics-monitor + namespace: project-system +spec: + endpoints: + - tlsConfig: + ca: + secret: + key: ca.crt + name: metrics-server-cert + cert: + secret: + key: tls.crt + name: metrics-server-cert + insecureSkipVerify: false + keySecret: + key: tls.key + name: metrics-server-cert + selector: + matchLabels: + app.kubernetes.io/name: project + control-plane: controller-manager +--- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: + annotations: + cert-manager.io/inject-ca-from: project-system/project-serving-cert name: project-mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -4172,6 +4290,8 @@ webhooks: apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: + annotations: + cert-manager.io/inject-ca-from: project-system/project-serving-cert name: project-validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/docs/book/src/cronjob-tutorial/testdata/project/go.mod b/docs/book/src/cronjob-tutorial/testdata/project/go.mod index c0f4bf539db..fd4c17f28ef 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/go.mod +++ b/docs/book/src/cronjob-tutorial/testdata/project/go.mod @@ -1,6 +1,8 @@ module tutorial.kubebuilder.io/project -go 1.22.0 +go 1.23.0 + +godebug default=go1.23 require ( github.com/onsi/ginkgo/v2 v2.19.0 @@ -9,7 +11,7 @@ require ( k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 - sigs.k8s.io/controller-runtime v0.19.1 + sigs.k8s.io/controller-runtime v0.19.4 ) require ( diff --git a/docs/book/src/cronjob-tutorial/testdata/project/go.sum b/docs/book/src/cronjob-tutorial/testdata/project/go.sum index 76feeae2ae7..e037e4a0e75 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/go.sum +++ b/docs/book/src/cronjob-tutorial/testdata/project/go.sum @@ -243,8 +243,8 @@ k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= -sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= +sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/docs/book/src/cronjob-tutorial/testdata/project/hack/boilerplate.go.txt b/docs/book/src/cronjob-tutorial/testdata/project/hack/boilerplate.go.txt index 0d32012672a..0a94b7b28b1 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/hack/boilerplate.go.txt +++ b/docs/book/src/cronjob-tutorial/testdata/project/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go index c85d8883994..4edf2325929 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -95,7 +95,7 @@ var ( // the user. // // For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/reconcile func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) diff --git a/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/suite_test.go b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/suite_test.go index 6c4e45c3d2e..6228c33d70d 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/suite_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,14 +22,12 @@ Kubebuilder scaffolded a `internal/controller/suite_test.go` file that does the First, it will contain the necessary imports. */ - package controller import ( "context" - "fmt" + "os" "path/filepath" - "runtime" "testing" ctrl "sigs.k8s.io/controller-runtime" @@ -58,12 +56,12 @@ Now, let's go through the code generated. */ var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment cfg *rest.Config k8sClient client.Client // You'll be using this client in your tests. - testEnv *envtest.Environment ) -var ctx context.Context -var cancel context.CancelFunc func TestControllers(t *testing.T) { RegisterFailHandler(Fail) @@ -76,45 +74,43 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error + /* + The CronJob Kind is added to the runtime scheme used by the test environment. + This ensures that the CronJob API is registered with the scheme, allowing the test controller to recognize and interact with CronJob resources. + */ + err = batchv1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + /* + After the schemas, you will see the following marker. + This marker is what allows new schemas to be added here automatically when a new API is added to the project. + */ + + // +kubebuilder:scaffold:scheme + /* - First, the envtest cluster is configured to read CRDs from the CRD directory Kubebuilder scaffolds for you. + The envtest environment is configured to load Custom Resource Definitions (CRDs) from the specified directory. + This setup enables the test environment to recognize and interact with the custom resources defined by these CRDs. */ By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, - - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), } + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } /* Then, we start the envtest cluster. */ - var err error + // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - /* - The autogenerated test code will add the CronJob Kind schema to the default client-go k8s scheme. - This ensures that the CronJob API/Kind will be used in our test controller. - */ - err = batchv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - /* - After the schemas, you will see the following marker. - This marker is what allows new schemas to be added here automatically when a new API is added to the project. - */ - - // +kubebuilder:scaffold:scheme - /* A client is created for our test CRUD operations. */ @@ -160,7 +156,6 @@ var _ = BeforeSuite(func() { err = k8sManager.Start(ctx) Expect(err).ToNot(HaveOccurred(), "failed to run manager") }() - }) /* @@ -178,3 +173,26 @@ var _ = AfterSuite(func() { /* Now that you have your controller running on a test cluster and a client ready to perform operations on your CronJob, we can start writing integration tests! */ + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go index 0ae648962cf..55b66e4c7d4 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -162,7 +162,7 @@ This marker is responsible for generating a validation webhook manifest. // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, // as this struct is used only for temporary operations and does not need to be deeply copied. type CronJobCustomValidator struct { - //TODO(user): Add more fields as needed for validation + // TODO(user): Add more fields as needed for validation } var _ webhook.CustomValidator = &CronJobCustomValidator{} diff --git a/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go index 5ae40bf80a7..da4421ba71e 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go index 1b47dd5c702..2f49aa1a62b 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ import ( "crypto/tls" "fmt" "net" + "os" "path/filepath" - "runtime" "testing" "time" @@ -30,10 +30,6 @@ import ( . "github.com/onsi/gomega" admissionv1 "k8s.io/api/admission/v1" - - batchv1 "tutorial.kubebuilder.io/project/api/v1" - - // +kubebuilder:scaffold:imports apimachineryruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -43,16 +39,19 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + + batchv1 "tutorial.kubebuilder.io/project/api/v1" + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var ( - cancel context.CancelFunc - cfg *rest.Config ctx context.Context + cancel context.CancelFunc k8sClient client.Client + cfg *rest.Config testEnv *envtest.Environment ) @@ -67,39 +66,36 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error + scheme := apimachineryruntime.NewScheme() + err = batchv1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + err = admissionv1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: false, - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), - WebhookInstallOptions: envtest.WebhookInstallOptions{ Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, }, } - var err error + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - scheme := apimachineryruntime.NewScheme() - err = batchv1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = admissionv1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) @@ -148,3 +144,26 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go index 265d1829d2c..21174e0b4d1 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -60,19 +60,9 @@ var _ = BeforeSuite(func() { By("Ensure that Prometheus is enabled") _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#") - By("generating files") - cmd := exec.Command("make", "generate") - _, err := utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate") - - By("generating manifests") - cmd = exec.Command("make", "manifests") - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests") - By("building the manager(Operator) image") - cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) - _, err = utils.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 diff --git a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go index bfc657fb749..f533d608250 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -46,13 +46,20 @@ var _ = Describe("Manager", Ordered, func() { var controllerPodName string // Before running the tests, set up the environment by creating the namespace, - // installing CRDs, and deploying the controller. + // enforce the restricted security policy to the namespace, installing CRDs, + // and deploying the controller. BeforeAll(func() { By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) _, 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 = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") + By("installing CRDs") cmd = exec.Command("make", "install") _, err = utils.Run(cmd) @@ -93,27 +100,27 @@ var _ = Describe("Manager", Ordered, func() { cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) controllerLogs, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Controller logs:\n %s", controllerLogs)) + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Controller logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err) } By("Fetching Kubernetes events") cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") eventsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Kubernetes events:\n%s", eventsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Kubernetes events: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err) } By("Fetching curl-metrics logs") cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) metricsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Metrics logs:\n %s", metricsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get curl-metrics logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err) } By("Fetching controller manager pod description") @@ -209,10 +216,30 @@ var _ = Describe("Manager", Ordered, func() { By("creating the curl-metrics pod to access the metrics endpoint") cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", "--namespace", namespace, - "--image=curlimages/curl:7.78.0", - "--", "/bin/sh", "-c", fmt.Sprintf( - "curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics", - token, metricsServiceName, namespace)) + "--image=curlimages/curl:latest", + "--overrides", + fmt.Sprintf(`{ + "spec": { + "containers": [{ + "name": "curl", + "image": "curlimages/curl:latest", + "command": ["/bin/sh", "-c"], + "args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"], + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + }], + "serviceAccount": "%s" + } + }`, token, metricsServiceName, namespace, serviceAccountName)) _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") @@ -316,7 +343,7 @@ func serviceAccountToken() (string, error) { // Parse the JSON output to extract the token var token tokenRequest - err = json.Unmarshal([]byte(output), &token) + err = json.Unmarshal(output, &token) g.Expect(err).NotTo(HaveOccurred()) out = token.Status.Token diff --git a/docs/book/src/cronjob-tutorial/testdata/project/test/utils/utils.go b/docs/book/src/cronjob-tutorial/testdata/project/test/utils/utils.go index a239bfd7be2..2367f92fafd 100644 --- a/docs/book/src/cronjob-tutorial/testdata/project/test/utils/utils.go +++ b/docs/book/src/cronjob-tutorial/testdata/project/test/utils/utils.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -92,7 +92,7 @@ func IsPrometheusCRDsInstalled() bool { if err != nil { return false } - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range prometheusCRDs { for _, line := range crdList { if strings.Contains(line, crd) { @@ -153,7 +153,7 @@ func IsCertManagerCRDsInstalled() bool { } // Check if any of the Cert Manager CRDs are present - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range certManagerCRDs { for _, line := range crdList { if strings.Contains(line, crd) { diff --git a/docs/book/src/faq.md b/docs/book/src/faq.md index b185f8d2133..3b7a55d7843 100644 --- a/docs/book/src/faq.md +++ b/docs/book/src/faq.md @@ -127,6 +127,36 @@ Your CRDs are generated using [controller-gen][controller-gen]. By using the opt You can review the design of your APIs and see if it has not more specs than should be by hurting single responsibility principle for example. So that you might to re-design them. +## How can I validate and parse fields in CRDs effectively? + +To enhance user experience, it is recommended to use [OpenAPI v3 schema](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#schemaObject) validation when writing your CRDs. However, this approach can sometimes require an additional parsing step. +For example, consider this code +```go +type StructName struct { + // +kubebuilder:validation:Format=date-time + TimeField string `json:"timeField,omitempty"` +} +``` + +### What happens in this scenario? + +- Users will receive an error notification from the Kubernetes API if they attempt to create a CRD with an invalid timeField value. +- On the developer side, the string value needs to be parsed manually before use. + +### Is there a better approach? + +To provide both a better user experience and a streamlined developer experience, it is advisable to use predefined types like [`metav1.Time`](https://pkg.go.dev/k8s.io/apimachinery@v0.31.1/pkg/apis/meta/v1#Time) +For example, consider this code +```go +type StructName struct { + TimeField metav1.Time `json:"timeField,omitempty"` +} +``` + +### What happens in this scenario? + +- Users still receive error notifications from the Kubernetes API for invalid `timeField` values. +- Developers can directly use the parsed TimeField in their code without additional parsing, reducing errors and improving efficiency. diff --git a/docs/book/src/getting-started.md b/docs/book/src/getting-started.md index 569eee62a8d..4a3670f24b6 100644 --- a/docs/book/src/getting-started.md +++ b/docs/book/src/getting-started.md @@ -116,7 +116,7 @@ Please ensure that you review: [Kubernetes API Conventions](https://github.com/k #### Markers and validations Furthermore, we want to validate the values added in our CustomResource -to ensure that those are valid. To do it we are will use refer [markers][markers], +to ensure that those are valid. To achieve this, we will use [markers][markers], such as `+kubebuilder:validation:Minimum=1`. Now, see our example fully completed. @@ -399,7 +399,7 @@ to refresh the files located under `config/rbac`. The [Manager][manager] in the `cmd/main.go` file is responsible for managing the controllers in your application. -
cmd/main.gol: Our main.go +
cmd/main.go: Our main.go ```go {{#include ./getting-started/testdata/project/cmd/main.go}} @@ -439,6 +439,6 @@ implemented for your controller. [quick-start]: ./quick-start.md [best-practices]: ./reference/good-practices.md [cronjob-tutorial]: https://book.kubebuilder.io/cronjob-tutorial/cronjob-tutorial.html -[deploy-image]: ./plugins/deploy-image-plugin-v1-alpha.md +[deploy-image]: ./plugins/available/deploy-image-plugin-v1-alpha.md [GOPATH-golang-docs]: https://golang.org/doc/code.html#GOPATH [go-modules-blogpost]: https://blog.golang.org/using-go-modules diff --git a/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json b/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json index e2cdc09c96c..259f0f67268 100644 --- a/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json +++ b/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Kubebuilder DevContainer", - "image": "golang:1.22", + "image": "golang:1.23", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} diff --git a/docs/book/src/getting-started/testdata/project/.github/workflows/lint.yml b/docs/book/src/getting-started/testdata/project/.github/workflows/lint.yml index f40d36579ce..46511063a49 100644 --- a/docs/book/src/getting-started/testdata/project/.github/workflows/lint.yml +++ b/docs/book/src/getting-started/testdata/project/.github/workflows/lint.yml @@ -15,9 +15,9 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Run linter uses: golangci/golangci-lint-action@v6 with: - version: v1.61 + version: v1.62.2 diff --git a/docs/book/src/getting-started/testdata/project/.github/workflows/test-e2e.yml b/docs/book/src/getting-started/testdata/project/.github/workflows/test-e2e.yml index 8780644002a..b2eda8c3db0 100644 --- a/docs/book/src/getting-started/testdata/project/.github/workflows/test-e2e.yml +++ b/docs/book/src/getting-started/testdata/project/.github/workflows/test-e2e.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | diff --git a/docs/book/src/getting-started/testdata/project/.github/workflows/test.yml b/docs/book/src/getting-started/testdata/project/.github/workflows/test.yml index 7baf6579399..fc2e80d304d 100644 --- a/docs/book/src/getting-started/testdata/project/.github/workflows/test.yml +++ b/docs/book/src/getting-started/testdata/project/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Running Tests run: | diff --git a/docs/book/src/getting-started/testdata/project/Dockerfile b/docs/book/src/getting-started/testdata/project/Dockerfile index 4ba18b68cc4..5c73c7f37cd 100644 --- a/docs/book/src/getting-started/testdata/project/Dockerfile +++ b/docs/book/src/getting-started/testdata/project/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/docs/book/src/getting-started/testdata/project/Makefile b/docs/book/src/getting-started/testdata/project/Makefile index 30cbd42173c..c99f86ca5ff 100644 --- a/docs/book/src/getting-started/testdata/project/Makefile +++ b/docs/book/src/getting-started/testdata/project/Makefile @@ -1,7 +1,5 @@ # Image URL to use all building/pushing image targets IMG ?= controller:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.31.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -60,7 +58,7 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. +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'. @@ -88,6 +86,10 @@ lint: golangci-lint ## Run golangci-lint linter 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 + ##@ Build .PHONY: build @@ -171,9 +173,12 @@ GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions KUSTOMIZE_VERSION ?= v5.5.0 -CONTROLLER_TOOLS_VERSION ?= v0.16.4 -ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.61.0 +CONTROLLER_TOOLS_VERSION ?= v0.17.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", $$3}') +GOLANGCI_LINT_VERSION ?= v1.62.2 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. @@ -185,6 +190,14 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(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; \ + } + .PHONY: envtest envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) diff --git a/docs/book/src/getting-started/testdata/project/README.md b/docs/book/src/getting-started/testdata/project/README.md index b5295ca3fa8..1d6e7bcd560 100644 --- a/docs/book/src/getting-started/testdata/project/README.md +++ b/docs/book/src/getting-started/testdata/project/README.md @@ -7,7 +7,7 @@ ## Getting Started ### Prerequisites -- go version v1.22.0+ +- go version v1.23.0+ - docker version 17.03+. - kubectl version v1.11.3+. - Access to a Kubernetes v1.11.3+ cluster. @@ -68,7 +68,9 @@ make undeploy ## Project Distribution -Following are the steps to build the installer and distribute this project to users. +Following the options to release and provide this solution to the users. + +### By providing a bundle with all YAML files 1. Build the installer for the image built and published in the registry: @@ -76,19 +78,38 @@ Following are the steps to build the installer and distribute this project to us make build-installer IMG=/project:tag ``` -NOTE: The makefile target mentioned above generates an 'install.yaml' +**NOTE:** The makefile target mentioned above generates an 'install.yaml' file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without -its dependencies. +with Kustomize, which are necessary to install this project without its +dependencies. 2. Using the installer -Users can just run kubectl apply -f to install the project, i.e.: +Users can just run 'kubectl apply -f ' to install +the project, i.e.: ```sh kubectl apply -f https://raw.githubusercontent.com//project//dist/install.yaml ``` +### By providing a Helm Chart + +1. Build the chart using the optional helm plugin + +```sh +kubebuilder edit --plugins=helm/v1-alpha +``` + +2. See that a chart was generated under 'dist/chart', and users +can obtain this solution from there. + +**NOTE:** If you change the project, you need to update the Helm Chart +using the same command above to sync the latest changes. Furthermore, +if you create webhooks, you need to use the above command with +the '--force' flag and manually ensure that any custom configuration +previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' +is manually re-applied afterwards. + ## Contributing // TODO(user): Add detailed information on how you would like others to contribute to this project @@ -98,7 +119,7 @@ More information can be found via the [Kubebuilder Documentation](https://book.k ## License -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/getting-started/testdata/project/api/v1alpha1/groupversion_info.go b/docs/book/src/getting-started/testdata/project/api/v1alpha1/groupversion_info.go index a8031fcefdd..e4fa7421e88 100644 --- a/docs/book/src/getting-started/testdata/project/api/v1alpha1/groupversion_info.go +++ b/docs/book/src/getting-started/testdata/project/api/v1alpha1/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/getting-started/testdata/project/api/v1alpha1/memcached_types.go b/docs/book/src/getting-started/testdata/project/api/v1alpha1/memcached_types.go index 48f14cacb1c..c327bb4d025 100644 --- a/docs/book/src/getting-started/testdata/project/api/v1alpha1/memcached_types.go +++ b/docs/book/src/getting-started/testdata/project/api/v1alpha1/memcached_types.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/getting-started/testdata/project/api/v1alpha1/zz_generated.deepcopy.go b/docs/book/src/getting-started/testdata/project/api/v1alpha1/zz_generated.deepcopy.go index f002d24b13e..0474674fab0 100644 --- a/docs/book/src/getting-started/testdata/project/api/v1alpha1/zz_generated.deepcopy.go +++ b/docs/book/src/getting-started/testdata/project/api/v1alpha1/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/getting-started/testdata/project/cmd/main.go b/docs/book/src/getting-started/testdata/project/cmd/main.go index d707ad754bc..31e35773236 100644 --- a/docs/book/src/getting-started/testdata/project/cmd/main.go +++ b/docs/book/src/getting-started/testdata/project/cmd/main.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import ( "crypto/tls" "flag" "os" + "path/filepath" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -29,6 +30,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -52,8 +54,11 @@ func init() { // +kubebuilder:scaffold:scheme } +// nolint:gocyclo func main() { var metricsAddr string + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string var enableLeaderElection bool var probeAddr string var secureMetrics bool @@ -67,6 +72,13 @@ func main() { "Enabling this will ensure there is only one active controller manager.") flag.BoolVar(&secureMetrics, "metrics-secure", true, "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") opts := zap.Options{ @@ -92,13 +104,38 @@ func main() { tlsOpts = append(tlsOpts, disableHTTP2) } + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + webhookServer := webhook.NewServer(webhook.Options{ - TLSOpts: tlsOpts, + TLSOpts: webhookTLSOpts, }) // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. // More info: - // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/metrics/server // - https://book.kubebuilder.io/reference/metrics.html metricsServerOptions := metricsserver.Options{ BindAddress: metricsAddr, @@ -110,12 +147,35 @@ func main() { // FilterProvider is used to protect the metrics endpoint with authn/authz. // These configurations ensure that only authorized users and service accounts // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: - // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/metrics/filters#WithAuthenticationAndAuthorization metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } - // TODO(user): If CertDir, CertName, and KeyName are not specified, controller-runtime will automatically - // generate self-signed certificates for the metrics server. While convenient for development and testing, - // this setup is not recommended for production. + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "to initialize metrics certificate watcher", "error", err) + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ @@ -151,6 +211,22 @@ func main() { } // +kubebuilder:scaffold:builder + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1) diff --git a/docs/book/src/getting-started/testdata/project/config/crd/bases/cache.example.com_memcacheds.yaml b/docs/book/src/getting-started/testdata/project/config/crd/bases/cache.example.com_memcacheds.yaml index 09aa4238d7f..fa355167005 100644 --- a/docs/book/src/getting-started/testdata/project/config/crd/bases/cache.example.com_memcacheds.yaml +++ b/docs/book/src/getting-started/testdata/project/config/crd/bases/cache.example.com_memcacheds.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.17.0 name: memcacheds.cache.example.com spec: group: cache.example.com diff --git a/docs/book/src/getting-started/testdata/project/config/crd/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/crd/kustomization.yaml index 217b2175494..bdf76e3b9ca 100644 --- a/docs/book/src/getting-started/testdata/project/config/crd/kustomization.yaml +++ b/docs/book/src/getting-started/testdata/project/config/crd/kustomization.yaml @@ -10,10 +10,6 @@ patches: # patches here are for enabling the conversion webhook for each CRD # +kubebuilder:scaffold:crdkustomizewebhookpatch -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -# +kubebuilder:scaffold:crdkustomizecainjectionpatch - # [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. #configurations: diff --git a/docs/book/src/getting-started/testdata/project/config/default/cert_metrics_manager_patch.yaml b/docs/book/src/getting-started/testdata/project/config/default/cert_metrics_manager_patch.yaml new file mode 100644 index 00000000000..d975015538e --- /dev/null +++ b/docs/book/src/getting-started/testdata/project/config/default/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/docs/book/src/getting-started/testdata/project/config/default/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/default/kustomization.yaml index 8922567ea88..dc250c032a8 100644 --- a/docs/book/src/getting-started/testdata/project/config/default/kustomization.yaml +++ b/docs/book/src/getting-started/testdata/project/config/default/kustomization.yaml @@ -33,7 +33,7 @@ resources: # be able to communicate with the Webhook Server. #- ../network-policy -# Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager +# Uncomment the patches line if you enable Metrics patches: # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. # More info: https://book.kubebuilder.io/reference/metrics @@ -41,13 +41,60 @@ patches: target: kind: Deployment +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +#- path: cert_metrics_manager_patch.yaml +# target: +# kind: Deployment + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml #- path: manager_webhook_patch.yaml +# target: +# kind: Deployment # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations #replacements: +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# # - source: # Uncomment the following block if you have any webhook # kind: Service # version: v1 @@ -58,6 +105,7 @@ patches: # kind: Certificate # group: cert-manager.io # version: v1 +# name: serving-cert # fieldPaths: # - .spec.dnsNames.0 # - .spec.dnsNames.1 @@ -75,6 +123,7 @@ patches: # kind: Certificate # group: cert-manager.io # version: v1 +# name: serving-cert # fieldPaths: # - .spec.dnsNames.0 # - .spec.dnsNames.1 @@ -102,7 +151,7 @@ patches: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.name # targets: # - select: @@ -118,7 +167,7 @@ patches: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.namespace # Namespace of the certificate CR # targets: # - select: @@ -133,7 +182,7 @@ patches: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.name # targets: # - select: @@ -149,29 +198,15 @@ patches: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns # - source: # kind: Certificate # group: cert-manager.io # version: v1 -# name: serving-cert # This name should match the one in certificate.yaml +# name: serving-cert # fieldPath: .metadata.name -# targets: -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/docs/book/src/getting-started/testdata/project/config/default/metrics_service.yaml b/docs/book/src/getting-started/testdata/project/config/default/metrics_service.yaml index 4425b9b8977..df85a7387c5 100644 --- a/docs/book/src/getting-started/testdata/project/config/default/metrics_service.yaml +++ b/docs/book/src/getting-started/testdata/project/config/default/metrics_service.yaml @@ -15,3 +15,4 @@ spec: targetPort: 8443 selector: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/getting-started/testdata/project/config/manager/manager.yaml b/docs/book/src/getting-started/testdata/project/config/manager/manager.yaml index 1bb9d5a6485..989f80ca5fe 100644 --- a/docs/book/src/getting-started/testdata/project/config/manager/manager.yaml +++ b/docs/book/src/getting-started/testdata/project/config/manager/manager.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project replicas: 1 template: metadata: @@ -27,6 +28,7 @@ spec: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager + app.kubernetes.io/name: project spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -49,14 +51,12 @@ spec: # values: # - linux securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - command: - /manager @@ -65,6 +65,7 @@ spec: - --health-probe-bind-address=:8081 image: controller:latest name: manager + ports: [] securityContext: allowPrivilegeEscalation: false capabilities: @@ -91,5 +92,7 @@ spec: requests: cpu: 10m memory: 64Mi + volumeMounts: [] + volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/docs/book/src/getting-started/testdata/project/config/network-policy/allow-metrics-traffic.yaml b/docs/book/src/getting-started/testdata/project/config/network-policy/allow-metrics-traffic.yaml index de6ec5f8097..0e64d74ef6e 100644 --- a/docs/book/src/getting-started/testdata/project/config/network-policy/allow-metrics-traffic.yaml +++ b/docs/book/src/getting-started/testdata/project/config/network-policy/allow-metrics-traffic.yaml @@ -1,6 +1,6 @@ # This NetworkPolicy allows ingress traffic # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those -# namespaces are able to gathering data from the metrics endpoint. +# namespaces are able to gather data from the metrics endpoint. apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: @@ -13,6 +13,7 @@ spec: podSelector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project policyTypes: - Ingress ingress: diff --git a/docs/book/src/getting-started/testdata/project/config/prometheus/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/prometheus/kustomization.yaml index ed137168a1d..fdc5481b103 100644 --- a/docs/book/src/getting-started/testdata/project/config/prometheus/kustomization.yaml +++ b/docs/book/src/getting-started/testdata/project/config/prometheus/kustomization.yaml @@ -1,2 +1,11 @@ resources: - monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +#patches: +# - path: monitor_tls_patch.yaml +# target: +# kind: ServiceMonitor diff --git a/docs/book/src/getting-started/testdata/project/config/prometheus/monitor.yaml b/docs/book/src/getting-started/testdata/project/config/prometheus/monitor.yaml index 1dea5d5fd7b..3a0798c331e 100644 --- a/docs/book/src/getting-started/testdata/project/config/prometheus/monitor.yaml +++ b/docs/book/src/getting-started/testdata/project/config/prometheus/monitor.yaml @@ -16,15 +16,12 @@ spec: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables - # certificate verification. This poses a significant security risk by making the system vulnerable to - # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between - # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, - # compromising the integrity and confidentiality of the information. - # Please use the following options for secure configurations: - # caFile: /etc/metrics-certs/ca.crt - # certFile: /etc/metrics-certs/tls.crt - # keyFile: /etc/metrics-certs/tls.key + # certificate verification, exposing the system to potential man-in-the-middle attacks. + # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. + # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, + # which securely references the certificate from the 'metrics-server-cert' secret. insecureSkipVerify: true selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/getting-started/testdata/project/config/prometheus/monitor_tls_patch.yaml b/docs/book/src/getting-started/testdata/project/config/prometheus/monitor_tls_patch.yaml new file mode 100644 index 00000000000..e824dd0ff86 --- /dev/null +++ b/docs/book/src/getting-started/testdata/project/config/prometheus/monitor_tls_patch.yaml @@ -0,0 +1,22 @@ +# Patch for Prometheus ServiceMonitor to enable secure TLS configuration +# using certificates managed by cert-manager +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - tlsConfig: + 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 diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/kustomization.yaml index 603bdba9fb5..d8975bd4b46 100644 --- a/docs/book/src/getting-started/testdata/project/config/rbac/kustomization.yaml +++ b/docs/book/src/getting-started/testdata/project/config/rbac/kustomization.yaml @@ -18,10 +18,11 @@ resources: - metrics_auth_role.yaml - metrics_auth_role_binding.yaml - metrics_reader_role.yaml -# For each CRD, "Editor" and "Viewer" roles are scaffolded by +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by # default, aiding admins in cluster management. Those roles are -# not used by the Project itself. You can comment the following lines +# not used by the {{ .ProjectName }} itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- memcached_admin_role.yaml - memcached_editor_role.yaml - memcached_viewer_role.yaml diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/memcached_admin_role.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_admin_role.yaml new file mode 100644 index 00000000000..f42561d75b9 --- /dev/null +++ b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project project itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over cache.example.com. +# 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: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: memcached-admin-role +rules: +- apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - '*' +- apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/memcached_editor_role.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_editor_role.yaml index 03058d077d2..a3542f23263 100644 --- a/docs/book/src/getting-started/testdata/project/config/rbac/memcached_editor_role.yaml +++ b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_editor_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to edit memcacheds. +# This rule is not used by the project project 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 cache.example.com. +# 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: diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/memcached_viewer_role.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_viewer_role.yaml index 8ec26d3ffd5..079cb76a538 100644 --- a/docs/book/src/getting-started/testdata/project/config/rbac/memcached_viewer_role.yaml +++ b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_viewer_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to view memcacheds. +# This rule is not used by the project project itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to cache.example.com 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: diff --git a/docs/book/src/getting-started/testdata/project/dist/install.yaml b/docs/book/src/getting-started/testdata/project/dist/install.yaml index b53b8dbdccf..58337b9baec 100644 --- a/docs/book/src/getting-started/testdata/project/dist/install.yaml +++ b/docs/book/src/getting-started/testdata/project/dist/install.yaml @@ -11,7 +11,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.17.0 name: memcacheds.cache.example.com spec: group: cache.example.com @@ -234,6 +234,27 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + name: project-memcached-admin-role +rules: +- apiGroups: + - cache.example.com + resources: + - memcacheds + verbs: + - '*' +- apiGroups: + - cache.example.com + resources: + - memcacheds/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole metadata: labels: app.kubernetes.io/managed-by: kustomize @@ -372,6 +393,7 @@ spec: protocol: TCP targetPort: 8443 selector: + app.kubernetes.io/name: project control-plane: controller-manager --- apiVersion: apps/v1 @@ -387,12 +409,14 @@ spec: replicas: 1 selector: matchLabels: + app.kubernetes.io/name: project control-plane: controller-manager template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: + app.kubernetes.io/name: project control-plane: controller-manager spec: containers: @@ -410,6 +434,7 @@ spec: initialDelaySeconds: 15 periodSeconds: 20 name: manager + ports: [] readinessProbe: httpGet: path: /readyz @@ -428,10 +453,14 @@ spec: capabilities: drop: - ALL + volumeMounts: [] securityContext: runAsNonRoot: true + seccompProfile: + type: RuntimeDefault serviceAccountName: project-controller-manager terminationGracePeriodSeconds: 10 + volumes: [] --- apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor @@ -452,4 +481,5 @@ spec: insecureSkipVerify: true selector: matchLabels: + app.kubernetes.io/name: project control-plane: controller-manager diff --git a/docs/book/src/getting-started/testdata/project/go.mod b/docs/book/src/getting-started/testdata/project/go.mod index 9ca9ba549ac..6b794adbb3f 100644 --- a/docs/book/src/getting-started/testdata/project/go.mod +++ b/docs/book/src/getting-started/testdata/project/go.mod @@ -1,6 +1,8 @@ module example.com/memcached -go 1.22.0 +go 1.23.0 + +godebug default=go1.23 require ( github.com/onsi/ginkgo/v2 v2.19.0 @@ -8,7 +10,8 @@ require ( k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 - sigs.k8s.io/controller-runtime v0.19.1 + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + sigs.k8s.io/controller-runtime v0.19.4 ) require ( @@ -90,7 +93,6 @@ require ( k8s.io/component-base v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/docs/book/src/getting-started/testdata/project/go.sum b/docs/book/src/getting-started/testdata/project/go.sum index 09586676388..ef8a690618c 100644 --- a/docs/book/src/getting-started/testdata/project/go.sum +++ b/docs/book/src/getting-started/testdata/project/go.sum @@ -241,8 +241,8 @@ k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= -sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= +sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/docs/book/src/getting-started/testdata/project/hack/boilerplate.go.txt b/docs/book/src/getting-started/testdata/project/hack/boilerplate.go.txt index 0d32012672a..0a94b7b28b1 100644 --- a/docs/book/src/getting-started/testdata/project/hack/boilerplate.go.txt +++ b/docs/book/src/getting-started/testdata/project/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller.go b/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller.go index c3805a28664..7a733a9f910 100644 --- a/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller.go +++ b/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "time" "k8s.io/apimachinery/pkg/runtime" @@ -39,8 +40,6 @@ import ( const ( // typeAvailableMemcached represents the status of the Deployment reconciliation typeAvailableMemcached = "Available" - // typeDegradedMemcached represents the status used when the custom resource is deleted and the finalizer operations are yet to occur. - typeDegradedMemcached = "Degraded" ) // MemcachedReconciler reconciles a Memcached object @@ -68,7 +67,7 @@ type MemcachedReconciler struct { // - About Controllers: https://kubernetes.io/docs/concepts/architecture/controller/ // // For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/reconcile func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) @@ -231,7 +230,7 @@ func (r *MemcachedReconciler) deploymentForMemcached( }, Spec: corev1.PodSpec{ SecurityContext: &corev1.PodSecurityContext{ - RunAsNonRoot: &[]bool{true}[0], + RunAsNonRoot: ptr.To(true), SeccompProfile: &corev1.SeccompProfile{ Type: corev1.SeccompProfileTypeRuntimeDefault, }, @@ -243,9 +242,9 @@ func (r *MemcachedReconciler) deploymentForMemcached( // Ensure restrictive context for the container // More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: &[]bool{true}[0], - RunAsUser: &[]int64{1001}[0], - AllowPrivilegeEscalation: &[]bool{false}[0], + RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(int64(1001)), + AllowPrivilegeEscalation: ptr.To(false), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{ "ALL", diff --git a/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller_test.go b/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller_test.go index 9fa08c97f9d..25811a50efe 100644 --- a/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller_test.go +++ b/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/getting-started/testdata/project/internal/controller/suite_test.go b/docs/book/src/getting-started/testdata/project/internal/controller/suite_test.go index e121ac136cd..d97f2ecae76 100644 --- a/docs/book/src/getting-started/testdata/project/internal/controller/suite_test.go +++ b/docs/book/src/getting-started/testdata/project/internal/controller/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,9 +18,8 @@ package controller import ( "context" - "fmt" + "os" "path/filepath" - "runtime" "testing" . "github.com/onsi/ginkgo/v2" @@ -40,11 +39,13 @@ import ( // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment -var ctx context.Context -var cancel context.CancelFunc +var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment + cfg *rest.Config + k8sClient client.Client +) func TestControllers(t *testing.T) { RegisterFailHandler(Fail) @@ -57,35 +58,31 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error + err = cachev1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, + } - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() } - var err error // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = cachev1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) - }) var _ = AfterSuite(func() { @@ -94,3 +91,26 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go index 5e3806b98ee..73d3845ebda 100644 --- a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go +++ b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -60,19 +60,9 @@ var _ = BeforeSuite(func() { By("Ensure that Prometheus is enabled") _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#") - By("generating files") - cmd := exec.Command("make", "generate") - _, err := utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate") - - By("generating manifests") - cmd = exec.Command("make", "manifests") - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests") - By("building the manager(Operator) image") - cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) - _, err = utils.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 diff --git a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go index eebb6bfa0ac..c2a9ebf38c6 100644 --- a/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go +++ b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -46,13 +46,20 @@ var _ = Describe("Manager", Ordered, func() { var controllerPodName string // Before running the tests, set up the environment by creating the namespace, - // installing CRDs, and deploying the controller. + // enforce the restricted security policy to the namespace, installing CRDs, + // and deploying the controller. BeforeAll(func() { By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) _, 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 = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") + By("installing CRDs") cmd = exec.Command("make", "install") _, err = utils.Run(cmd) @@ -93,27 +100,27 @@ var _ = Describe("Manager", Ordered, func() { cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) controllerLogs, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Controller logs:\n %s", controllerLogs)) + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Controller logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err) } By("Fetching Kubernetes events") cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") eventsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Kubernetes events:\n%s", eventsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Kubernetes events: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err) } By("Fetching curl-metrics logs") cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) metricsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Metrics logs:\n %s", metricsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get curl-metrics logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err) } By("Fetching controller manager pod description") @@ -209,10 +216,30 @@ var _ = Describe("Manager", Ordered, func() { By("creating the curl-metrics pod to access the metrics endpoint") cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", "--namespace", namespace, - "--image=curlimages/curl:7.78.0", - "--", "/bin/sh", "-c", fmt.Sprintf( - "curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics", - token, metricsServiceName, namespace)) + "--image=curlimages/curl:latest", + "--overrides", + fmt.Sprintf(`{ + "spec": { + "containers": [{ + "name": "curl", + "image": "curlimages/curl:latest", + "command": ["/bin/sh", "-c"], + "args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"], + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + }], + "serviceAccount": "%s" + } + }`, token, metricsServiceName, namespace, serviceAccountName)) _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") @@ -278,7 +305,7 @@ func serviceAccountToken() (string, error) { // Parse the JSON output to extract the token var token tokenRequest - err = json.Unmarshal([]byte(output), &token) + err = json.Unmarshal(output, &token) g.Expect(err).NotTo(HaveOccurred()) out = token.Status.Token diff --git a/docs/book/src/getting-started/testdata/project/test/utils/utils.go b/docs/book/src/getting-started/testdata/project/test/utils/utils.go index a239bfd7be2..2367f92fafd 100644 --- a/docs/book/src/getting-started/testdata/project/test/utils/utils.go +++ b/docs/book/src/getting-started/testdata/project/test/utils/utils.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -92,7 +92,7 @@ func IsPrometheusCRDsInstalled() bool { if err != nil { return false } - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range prometheusCRDs { for _, line := range crdList { if strings.Contains(line, crd) { @@ -153,7 +153,7 @@ func IsCertManagerCRDsInstalled() bool { } // Check if any of the Cert Manager CRDs are present - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range certManagerCRDs { for _, line := range crdList { if strings.Contains(line, crd) { diff --git a/docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md b/docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md index 2b69a7cd0dc..502f5bdbfe6 100644 --- a/docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md +++ b/docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md @@ -720,7 +720,7 @@ Now, re-create the APIS(CRDs) and Webhooks manifests by running the `kubebuilde [controller-releases]: https://github.com/kubernetes-sigs/controller-runtime/releases [issue-1893]: https://github.com/kubernetes-sigs/kubebuilder/issues/1839 [plugins-doc]: /reference/cli-plugins.md -[migration-v2vsv3]: /migration/v2vsv3.md +[migration-v2vsv3]: v2vsv3.md [custom-resource-definition-versioning]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/ [issue-1999]: https://github.com/kubernetes-sigs/kubebuilder/issues/1999 [project-customizations]: v2vsv3.md#project-customizations diff --git a/docs/book/src/migration/v2vsv3.md b/docs/book/src/migration/legacy/v2vsv3.md similarity index 100% rename from docs/book/src/migration/v2vsv3.md rename to docs/book/src/migration/legacy/v2vsv3.md diff --git a/docs/book/src/multiversion-tutorial/conversion.md b/docs/book/src/multiversion-tutorial/conversion.md index 443b2a79284..39b37a907b2 100644 --- a/docs/book/src/multiversion-tutorial/conversion.md +++ b/docs/book/src/multiversion-tutorial/conversion.md @@ -1,8 +1,16 @@ # Implementing conversion With our model for conversion in place, it's time to actually implement -the conversion functions. We'll put them in a file called -`cronjob_conversion.go` next to our `cronjob_types.go` file, to avoid +the conversion functions. We'll create a conversion webhook +for our CronJob API version `v1` (Hub) to Spoke our CronJob API version +`v2` see: + +```go +kubebuilder create webhook --group batch --version v1 --kind CronJob --conversion --spoke v2 +``` + +The above command will generate the `cronjob_conversion.go` next to our +`cronjob_types.go` file, to avoid cluttering up our main types file with extra functions. ## Hub... diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json b/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json index e2cdc09c96c..259f0f67268 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json +++ b/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Kubebuilder DevContainer", - "image": "golang:1.22", + "image": "golang:1.23", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/lint.yml b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/lint.yml index f40d36579ce..46511063a49 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/lint.yml +++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/lint.yml @@ -15,9 +15,9 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Run linter uses: golangci/golangci-lint-action@v6 with: - version: v1.61 + version: v1.62.2 diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-e2e.yml b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-e2e.yml index 8780644002a..b2eda8c3db0 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-e2e.yml +++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-e2e.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Install the latest version of kind run: | diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test.yml b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test.yml index 7baf6579399..fc2e80d304d 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test.yml +++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '~1.22' + go-version-file: go.mod - name: Running Tests run: | diff --git a/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile b/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile index 4ba18b68cc4..5c73c7f37cd 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile +++ b/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.22 AS builder +FROM golang:1.23 AS builder ARG TARGETOS ARG TARGETARCH diff --git a/docs/book/src/multiversion-tutorial/testdata/project/Makefile b/docs/book/src/multiversion-tutorial/testdata/project/Makefile index ba36d148420..230d92c319b 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/Makefile +++ b/docs/book/src/multiversion-tutorial/testdata/project/Makefile @@ -1,7 +1,5 @@ # Image URL to use all building/pushing image targets IMG ?= controller:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.31.0 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -64,7 +62,7 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. +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'. @@ -92,6 +90,10 @@ lint: golangci-lint ## Run golangci-lint linter 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 + ##@ Build .PHONY: build @@ -175,9 +177,12 @@ GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions KUSTOMIZE_VERSION ?= v5.5.0 -CONTROLLER_TOOLS_VERSION ?= v0.16.4 -ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.61.0 +CONTROLLER_TOOLS_VERSION ?= v0.17.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", $$3}') +GOLANGCI_LINT_VERSION ?= v1.62.2 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. @@ -189,6 +194,14 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(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; \ + } + .PHONY: envtest envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) diff --git a/docs/book/src/multiversion-tutorial/testdata/project/PROJECT b/docs/book/src/multiversion-tutorial/testdata/project/PROJECT index 867776e2af8..83cd75144ca 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/PROJECT +++ b/docs/book/src/multiversion-tutorial/testdata/project/PROJECT @@ -18,7 +18,10 @@ resources: path: tutorial.kubebuilder.io/project/api/v1 version: v1 webhooks: + conversion: true defaulting: true + spoke: + - v2 validation: true webhookVersion: v1 - api: @@ -30,7 +33,6 @@ resources: path: tutorial.kubebuilder.io/project/api/v2 version: v2 webhooks: - conversion: true defaulting: true validation: true webhookVersion: v1 diff --git a/docs/book/src/multiversion-tutorial/testdata/project/README.md b/docs/book/src/multiversion-tutorial/testdata/project/README.md index b5295ca3fa8..1d6e7bcd560 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/README.md +++ b/docs/book/src/multiversion-tutorial/testdata/project/README.md @@ -7,7 +7,7 @@ ## Getting Started ### Prerequisites -- go version v1.22.0+ +- go version v1.23.0+ - docker version 17.03+. - kubectl version v1.11.3+. - Access to a Kubernetes v1.11.3+ cluster. @@ -68,7 +68,9 @@ make undeploy ## Project Distribution -Following are the steps to build the installer and distribute this project to users. +Following the options to release and provide this solution to the users. + +### By providing a bundle with all YAML files 1. Build the installer for the image built and published in the registry: @@ -76,19 +78,38 @@ Following are the steps to build the installer and distribute this project to us make build-installer IMG=/project:tag ``` -NOTE: The makefile target mentioned above generates an 'install.yaml' +**NOTE:** The makefile target mentioned above generates an 'install.yaml' file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without -its dependencies. +with Kustomize, which are necessary to install this project without its +dependencies. 2. Using the installer -Users can just run kubectl apply -f to install the project, i.e.: +Users can just run 'kubectl apply -f ' to install +the project, i.e.: ```sh kubectl apply -f https://raw.githubusercontent.com//project//dist/install.yaml ``` +### By providing a Helm Chart + +1. Build the chart using the optional helm plugin + +```sh +kubebuilder edit --plugins=helm/v1-alpha +``` + +2. See that a chart was generated under 'dist/chart', and users +can obtain this solution from there. + +**NOTE:** If you change the project, you need to update the Helm Chart +using the same command above to sync the latest changes. Furthermore, +if you create webhooks, you need to use the above command with +the '--force' flag and manually ensure that any custom configuration +previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' +is manually re-applied afterwards. + ## Contributing // TODO(user): Add detailed information on how you would like others to contribute to this project @@ -98,7 +119,7 @@ More information can be found via the [Kubebuilder Documentation](https://book.k ## License -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_conversion.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_conversion.go index 10524383e34..69d6b98309a 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_conversion.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_conversion.go @@ -1,4 +1,6 @@ /* +Copyright 2025 The Kubernetes authors. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_types.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_types.go index 8f3e38935bf..212ab245292 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_types.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_types.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -127,6 +127,8 @@ type CronJobStatus struct { */ // +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:conversion:hub // +kubebuilder:subresource:status // +versionName=v1 // +kubebuilder:storageversion diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/groupversion_info.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/groupversion_info.go index 0c46f1df6ce..b1fcbf8fed5 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/groupversion_info.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go index 5f32c3ab478..740762cd3ce 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_conversion.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_conversion.go index 28fa9d6520b..c7315146c3b 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_conversion.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_conversion.go @@ -1,4 +1,6 @@ /* +Copyright 2025 The Kubernetes authors. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,16 +23,17 @@ For imports, we'll need the controller-runtime package, plus the API version for our hub type (v1), and finally some of the standard packages. */ + import ( "fmt" "strings" - "sigs.k8s.io/controller-runtime/pkg/conversion" + "log" - v1 "tutorial.kubebuilder.io/project/api/v1" -) + "sigs.k8s.io/controller-runtime/pkg/conversion" -// +kubebuilder:docs-gen:collapse=Imports + batchv1 "tutorial.kubebuilder.io/project/api/v1" +) // +kubebuilder:docs-gen:collapse=Imports /* Our "spoke" versions need to implement the @@ -43,9 +46,12 @@ methods to convert to/from the hub version. ConvertTo is expected to modify its argument to contain the converted object. Most of the conversion is straightforward copying, except for converting our changed field. */ -// ConvertTo converts this CronJob to the Hub version (v1). + +// ConvertTo converts this CronJob (v2) to the Hub version (v1). func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v1.CronJob) + dst := dstRaw.(*batchv1.CronJob) + log.Printf("ConvertTo: Converting CronJob from Spoke version v2 to Hub version v1;"+ + "source: %s/%s, target: %s/%s", src.Namespace, src.Name, dst.Namespace, dst.Name) sched := src.Spec.Schedule scheduleParts := []string{"*", "*", "*", "*", "*"} @@ -74,7 +80,7 @@ func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error { // Spec dst.Spec.StartingDeadlineSeconds = src.Spec.StartingDeadlineSeconds - dst.Spec.ConcurrencyPolicy = v1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy) + dst.Spec.ConcurrencyPolicy = batchv1.ConcurrencyPolicy(src.Spec.ConcurrencyPolicy) dst.Spec.Suspend = src.Spec.Suspend dst.Spec.JobTemplate = src.Spec.JobTemplate dst.Spec.SuccessfulJobsHistoryLimit = src.Spec.SuccessfulJobsHistoryLimit @@ -93,9 +99,11 @@ ConvertFrom is expected to modify its receiver to contain the converted object. Most of the conversion is straightforward copying, except for converting our changed field. */ -// ConvertFrom converts from the Hub version (v1) to this version. +// ConvertFrom converts the Hub version (v1) to this CronJob (v2). func (dst *CronJob) ConvertFrom(srcRaw conversion.Hub) error { - src := srcRaw.(*v1.CronJob) + src := srcRaw.(*batchv1.CronJob) + log.Printf("ConvertFrom: Converting CronJob from Hub version v1 to Spoke version v2;"+ + "source: %s/%s, target: %s/%s", src.Namespace, src.Name, dst.Namespace, dst.Name) schedParts := strings.Split(src.Spec.Schedule, " ") if len(schedParts) != 5 { diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_types.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_types.go index a28d72680f0..d36dbfdd4f3 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_types.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_types.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/groupversion_info.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/groupversion_info.go index 76084f4494c..0a3681a38e1 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/groupversion_info.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/zz_generated.deepcopy.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/zz_generated.deepcopy.go index 5ea5cddb2d2..36b1daef04c 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/zz_generated.deepcopy.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go b/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go index 1a002ab638d..c390b53cc7f 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import ( "crypto/tls" "flag" "os" + "path/filepath" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -31,6 +32,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -69,10 +71,13 @@ func init() { /* */ +// nolint:gocyclo func main() { /* */ var metricsAddr string + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string var enableLeaderElection bool var probeAddr string var secureMetrics bool @@ -86,6 +91,13 @@ func main() { "Enabling this will ensure there is only one active controller manager.") flag.BoolVar(&secureMetrics, "metrics-secure", true, "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") opts := zap.Options{ @@ -111,13 +123,38 @@ func main() { tlsOpts = append(tlsOpts, disableHTTP2) } + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + webhookServer := webhook.NewServer(webhook.Options{ - TLSOpts: tlsOpts, + TLSOpts: webhookTLSOpts, }) // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. // More info: - // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/metrics/server // - https://book.kubebuilder.io/reference/metrics.html metricsServerOptions := metricsserver.Options{ BindAddress: metricsAddr, @@ -129,12 +166,35 @@ func main() { // FilterProvider is used to protect the metrics endpoint with authn/authz. // These configurations ensure that only authorized users and service accounts // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: - // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/metrics/filters#WithAuthenticationAndAuthorization metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } - // TODO(user): If CertDir, CertName, and KeyName are not specified, controller-runtime will automatically - // generate self-signed certificates for the metrics server. While convenient for development and testing, - // this setup is not recommended for production. + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "to initialize metrics certificate watcher", "error", err) + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ @@ -192,6 +252,22 @@ func main() { /* */ + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") os.Exit(1) diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-metrics.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-metrics.yaml new file mode 100644 index 00000000000..f05703fa73e --- /dev/null +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-metrics.yaml @@ -0,0 +1,20 @@ +# The following manifests contain a self-signed issuer CR and a metrics certificate CR. +# More document can be found at https://docs.cert-manager.io +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + dnsNames: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. + - SERVICE_NAME.SERVICE_NAMESPACE.svc + - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml similarity index 50% rename from docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate.yaml rename to docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml index b51082a01e6..ae025c9c6ed 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml @@ -1,35 +1,20 @@ # The following manifests contain a self-signed issuer CR and a certificate CR. # More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - labels: - app.kubernetes.io/name: project - app.kubernetes.io/managed-by: kustomize - name: selfsigned-issuer - namespace: system -spec: - selfSigned: {} ---- apiVersion: cert-manager.io/v1 kind: Certificate metadata: labels: - app.kubernetes.io/name: certificate - app.kubernetes.io/instance: serving-cert - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: project - app.kubernetes.io/part-of: project + app.kubernetes.io/name: project app.kubernetes.io/managed-by: kustomize name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml namespace: system spec: # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + # replacements in the config/default/kustomization.yaml file. dnsNames: - SERVICE_NAME.SERVICE_NAMESPACE.svc - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local issuerRef: kind: Issuer name: selfsigned-issuer - secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize + secretName: webhook-server-cert diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/issuer.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/issuer.yaml new file mode 100644 index 00000000000..1c600ce5a67 --- /dev/null +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/issuer.yaml @@ -0,0 +1,13 @@ +# The following manifest contains a self-signed issuer CR. +# More information can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomization.yaml index bebea5a595e..fcb7498e468 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomization.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomization.yaml @@ -1,5 +1,7 @@ resources: -- certificate.yaml +- issuer.yaml +- certificate-webhook.yaml +- certificate-metrics.yaml configurations: - kustomizeconfig.yaml diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml index 1c55d60dfc5..31138d51a86 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.17.0 name: cronjobs.batch.tutorial.kubebuilder.io spec: group: batch.tutorial.kubebuilder.io diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/kustomization.yaml index 4cae15b8d87..575332229dd 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/kustomization.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/kustomization.yaml @@ -11,11 +11,6 @@ patches: - path: patches/webhook_in_cronjobs.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -#- path: patches/cainjection_in_cronjobs.yaml -# +kubebuilder:scaffold:crdkustomizecainjectionpatch - # [WEBHOOK] To enable webhook, uncomment the following section # the following config is for teaching kustomize how to do kustomization for CRDs. configurations: diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml deleted file mode 100644 index 752fa9ac6a0..00000000000 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME - name: cronjobs.batch.tutorial.kubebuilder.io diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/cert_metrics_manager_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/cert_metrics_manager_patch.yaml new file mode 100644 index 00000000000..d975015538e --- /dev/null +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/default/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/kustomization.yaml index 094f86a8cec..f72c5a8890f 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/default/kustomization.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/default/kustomization.yaml @@ -33,7 +33,7 @@ resources: # be able to communicate with the Webhook Server. #- ../network-policy -# Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager +# Uncomment the patches line if you enable Metrics patches: # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. # More info: https://book.kubebuilder.io/reference/metrics @@ -41,13 +41,60 @@ patches: target: kind: Deployment +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +- path: cert_metrics_manager_patch.yaml + target: + kind: Deployment + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml - path: manager_webhook_patch.yaml + target: + kind: Deployment # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations replacements: + - source: # Uncomment the following block to enable certificates for metrics + kind: Service + version: v1 + name: controller-manager-metrics-service + fieldPath: metadata.name + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + name: metrics-certs + fieldPaths: + - spec.dnsNames.0 + - spec.dnsNames.1 + options: + delimiter: '.' + index: 0 + create: true + + - source: + kind: Service + version: v1 + name: controller-manager-metrics-service + fieldPath: metadata.namespace + targets: + - select: + kind: Certificate + group: cert-manager.io + version: v1 + name: metrics-certs + fieldPaths: + - spec.dnsNames.0 + - spec.dnsNames.1 + options: + delimiter: '.' + index: 1 + create: true + - source: # Uncomment the following block if you have any webhook kind: Service version: v1 @@ -58,6 +105,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 + name: serving-cert fieldPaths: - .spec.dnsNames.0 - .spec.dnsNames.1 @@ -75,6 +123,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 + name: serving-cert fieldPaths: - .spec.dnsNames.0 - .spec.dnsNames.1 @@ -102,7 +151,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.name targets: - select: @@ -118,7 +167,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.namespace # Namespace of the certificate CR targets: - select: @@ -133,7 +182,7 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.name targets: - select: @@ -149,29 +198,33 @@ replacements: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.namespace # Namespace of the certificate CR - targets: + targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. - select: kind: CustomResourceDefinition + name: cronjobs.batch.tutorial.kubebuilder.io fieldPaths: - .metadata.annotations.[cert-manager.io/inject-ca-from] options: delimiter: '/' index: 0 create: true +# +kubebuilder:scaffold:crdkustomizecainjectionns - source: kind: Certificate group: cert-manager.io version: v1 - name: serving-cert # This name should match the one in certificate.yaml + name: serving-cert fieldPath: .metadata.name - targets: + targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. - select: kind: CustomResourceDefinition + name: cronjobs.batch.tutorial.kubebuilder.io fieldPaths: - .metadata.annotations.[cert-manager.io/inject-ca-from] options: delimiter: '/' index: 1 create: true +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_webhook_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_webhook_patch.yaml index 06ab33e4e87..963c8a4cc63 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_webhook_patch.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_webhook_patch.yaml @@ -1,26 +1,31 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system - labels: - app.kubernetes.io/name: project - app.kubernetes.io/managed-by: kustomize -spec: - template: - spec: - containers: - - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert +# This patch ensures the webhook certificates are properly mounted in the manager container. +# It configures the necessary arguments, volumes, volume mounts, and container ports. + +# Add the --webhook-cert-path argument for configuring the webhook certificate path +- op: add + path: /spec/template/spec/containers/0/args/- + value: --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs + +# Add the volumeMount for the webhook certificates +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-certs + readOnly: true + +# Add the port configuration for the webhook server +- op: add + path: /spec/template/spec/containers/0/ports/- + value: + containerPort: 9443 + name: webhook-server + protocol: TCP + +# Add the volume configuration for the webhook certificates +- op: add + path: /spec/template/spec/volumes/- + value: + name: webhook-certs + secret: + secretName: webhook-server-cert diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/metrics_service.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/metrics_service.yaml index 4425b9b8977..df85a7387c5 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/default/metrics_service.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/default/metrics_service.yaml @@ -15,3 +15,4 @@ spec: targetPort: 8443 selector: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/manager/manager.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/manager/manager.yaml index 1bb9d5a6485..989f80ca5fe 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/manager/manager.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/manager/manager.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project replicas: 1 template: metadata: @@ -27,6 +28,7 @@ spec: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager + app.kubernetes.io/name: project spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -49,14 +51,12 @@ spec: # values: # - linux securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - command: - /manager @@ -65,6 +65,7 @@ spec: - --health-probe-bind-address=:8081 image: controller:latest name: manager + ports: [] securityContext: allowPrivilegeEscalation: false capabilities: @@ -91,5 +92,7 @@ spec: requests: cpu: 10m memory: 64Mi + volumeMounts: [] + volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml index de6ec5f8097..0e64d74ef6e 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml @@ -1,6 +1,6 @@ # This NetworkPolicy allows ingress traffic # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those -# namespaces are able to gathering data from the metrics endpoint. +# namespaces are able to gather data from the metrics endpoint. apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: @@ -13,6 +13,7 @@ spec: podSelector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project policyTypes: - Ingress ingress: diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml index 4de86e58119..eb9464b64ea 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml @@ -13,6 +13,7 @@ spec: podSelector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project policyTypes: - Ingress ingress: diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/kustomization.yaml index ed137168a1d..8126ea89b1a 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/kustomization.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/kustomization.yaml @@ -1,2 +1,11 @@ resources: - monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +patches: + - path: monitor_tls_patch.yaml + target: + kind: ServiceMonitor diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor.yaml index 1dea5d5fd7b..3a0798c331e 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor.yaml @@ -16,15 +16,12 @@ spec: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables - # certificate verification. This poses a significant security risk by making the system vulnerable to - # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between - # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, - # compromising the integrity and confidentiality of the information. - # Please use the following options for secure configurations: - # caFile: /etc/metrics-certs/ca.crt - # certFile: /etc/metrics-certs/tls.crt - # keyFile: /etc/metrics-certs/tls.key + # certificate verification, exposing the system to potential man-in-the-middle attacks. + # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. + # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, + # which securely references the certificate from the 'metrics-server-cert' secret. insecureSkipVerify: true selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml new file mode 100644 index 00000000000..e824dd0ff86 --- /dev/null +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml @@ -0,0 +1,22 @@ +# Patch for Prometheus ServiceMonitor to enable secure TLS configuration +# using certificates managed by cert-manager +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - tlsConfig: + 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 diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_admin_role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_admin_role.yaml new file mode 100644 index 00000000000..234d656da08 --- /dev/null +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project project itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over batch.tutorial.kubebuilder.io. +# 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: + app.kubernetes.io/name: project + app.kubernetes.io/managed-by: kustomize + name: cronjob-admin-role +rules: +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs + verbs: + - '*' +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs/status + verbs: + - get diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml index c36d86e55d6..f0ccbbe8662 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to edit cronjobs. +# This rule is not used by the project project 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 batch.tutorial.kubebuilder.io. +# 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: diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml index 0bfb9809718..d8200790df3 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to view cronjobs. +# This rule is not used by the project project itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to batch.tutorial.kubebuilder.io 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: diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/kustomization.yaml index 39fe987357a..53466ccd0ac 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/kustomization.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/kustomization.yaml @@ -18,10 +18,11 @@ resources: - metrics_auth_role.yaml - metrics_auth_role_binding.yaml - metrics_reader_role.yaml -# For each CRD, "Editor" and "Viewer" roles are scaffolded by +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by # default, aiding admins in cluster management. Those roles are -# not used by the Project itself. You can comment the following lines +# not used by the {{ .ProjectName }} itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- cronjob_admin_role.yaml - cronjob_editor_role.yaml - cronjob_viewer_role.yaml diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/service.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/service.yaml index 1c94b010702..34c7252616d 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/service.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/service.yaml @@ -13,3 +13,4 @@ spec: targetPort: 9443 selector: control-plane: controller-manager + app.kubernetes.io/name: project diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml index 1345f29120c..b5fbaa31f34 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml +++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml @@ -12,7 +12,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: project-system/project-serving-cert - controller-gen.kubebuilder.io/version: v0.16.4 + controller-gen.kubebuilder.io/version: v0.17.0 name: cronjobs.batch.tutorial.kubebuilder.io spec: conversion: @@ -7676,6 +7676,27 @@ rules: --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project + name: project-cronjob-admin-role +rules: +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs + verbs: + - '*' +- apiGroups: + - batch.tutorial.kubebuilder.io + resources: + - cronjobs/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole metadata: labels: app.kubernetes.io/managed-by: kustomize @@ -7864,6 +7885,7 @@ spec: protocol: TCP targetPort: 8443 selector: + app.kubernetes.io/name: project control-plane: controller-manager --- apiVersion: v1 @@ -7880,6 +7902,7 @@ spec: protocol: TCP targetPort: 9443 selector: + app.kubernetes.io/name: project control-plane: controller-manager --- apiVersion: apps/v1 @@ -7895,12 +7918,14 @@ spec: replicas: 1 selector: matchLabels: + app.kubernetes.io/name: project control-plane: controller-manager template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: + app.kubernetes.io/name: project control-plane: controller-manager spec: containers: @@ -7908,6 +7933,8 @@ spec: - --metrics-bind-address=:8443 - --leader-elect - --health-probe-bind-address=:8081 + - --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + - --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs command: - /manager image: controller:latest @@ -7941,29 +7968,57 @@ spec: drop: - ALL volumeMounts: + - mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert + name: webhook-certs readOnly: true securityContext: runAsNonRoot: true + seccompProfile: + type: RuntimeDefault serviceAccountName: project-controller-manager terminationGracePeriodSeconds: 10 volumes: - - name: cert + - name: metrics-certs + secret: + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key + optional: false + secretName: metrics-server-cert + - name: webhook-certs secret: - defaultMode: 420 secretName: webhook-server-cert --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: labels: - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: project - app.kubernetes.io/instance: serving-cert app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/name: certificate - app.kubernetes.io/part-of: project + app.kubernetes.io/name: project + name: project-metrics-certs + namespace: project-system +spec: + dnsNames: + - project-controller-manager-metrics-service.project-system.svc + - project-controller-manager-metrics-service.project-system.svc.cluster.local + issuerRef: + kind: Issuer + name: project-selfsigned-issuer + secretName: metrics-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: project name: project-serving-cert namespace: project-system spec: @@ -7997,14 +8052,22 @@ metadata: namespace: project-system spec: endpoints: - - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - path: /metrics - port: https - scheme: https - tlsConfig: - insecureSkipVerify: true + - tlsConfig: + ca: + secret: + key: ca.crt + name: metrics-server-cert + cert: + secret: + key: tls.crt + name: metrics-server-cert + insecureSkipVerify: false + keySecret: + key: tls.key + name: metrics-server-cert selector: matchLabels: + app.kubernetes.io/name: project control-plane: controller-manager --- apiVersion: admissionregistration.k8s.io/v1 diff --git a/docs/book/src/multiversion-tutorial/testdata/project/go.mod b/docs/book/src/multiversion-tutorial/testdata/project/go.mod index c0f4bf539db..fd4c17f28ef 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/go.mod +++ b/docs/book/src/multiversion-tutorial/testdata/project/go.mod @@ -1,6 +1,8 @@ module tutorial.kubebuilder.io/project -go 1.22.0 +go 1.23.0 + +godebug default=go1.23 require ( github.com/onsi/ginkgo/v2 v2.19.0 @@ -9,7 +11,7 @@ require ( k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 - sigs.k8s.io/controller-runtime v0.19.1 + sigs.k8s.io/controller-runtime v0.19.4 ) require ( diff --git a/docs/book/src/multiversion-tutorial/testdata/project/go.sum b/docs/book/src/multiversion-tutorial/testdata/project/go.sum index 76feeae2ae7..e037e4a0e75 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/go.sum +++ b/docs/book/src/multiversion-tutorial/testdata/project/go.sum @@ -243,8 +243,8 @@ k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= -sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= +sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/docs/book/src/multiversion-tutorial/testdata/project/hack/boilerplate.go.txt b/docs/book/src/multiversion-tutorial/testdata/project/hack/boilerplate.go.txt index 0d32012672a..0a94b7b28b1 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/hack/boilerplate.go.txt +++ b/docs/book/src/multiversion-tutorial/testdata/project/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller.go index c85d8883994..4edf2325929 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -95,7 +95,7 @@ var ( // the user. // // For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/reconcile func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := log.FromContext(ctx) diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/suite_test.go index 528ad881da2..6228c33d70d 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/suite_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,9 +26,8 @@ package controller import ( "context" - "fmt" + "os" "path/filepath" - "runtime" "testing" ctrl "sigs.k8s.io/controller-runtime" @@ -57,12 +56,12 @@ Now, let's go through the code generated. */ var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment cfg *rest.Config k8sClient client.Client // You'll be using this client in your tests. - testEnv *envtest.Environment ) -var ctx context.Context -var cancel context.CancelFunc func TestControllers(t *testing.T) { RegisterFailHandler(Fail) @@ -75,45 +74,43 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error /* - First, the envtest cluster is configured to read CRDs from the CRD directory Kubebuilder scaffolds for you. + The CronJob Kind is added to the runtime scheme used by the test environment. + This ensures that the CronJob API is registered with the scheme, allowing the test controller to recognize and interact with CronJob resources. + */ + err = batchv1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + /* + After the schemas, you will see the following marker. + This marker is what allows new schemas to be added here automatically when a new API is added to the project. + */ + + // +kubebuilder:scaffold:scheme + + /* + The envtest environment is configured to load Custom Resource Definitions (CRDs) from the specified directory. + This setup enables the test environment to recognize and interact with the custom resources defined by these CRDs. */ By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, - - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), } + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } /* Then, we start the envtest cluster. */ - var err error + // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - /* - The autogenerated test code will add the CronJob Kind schema to the default client-go k8s scheme. - This ensures that the CronJob API/Kind will be used in our test controller. - */ - err = batchv1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - /* - After the schemas, you will see the following marker. - This marker is what allows new schemas to be added here automatically when a new API is added to the project. - */ - - // +kubebuilder:scaffold:scheme - /* A client is created for our test CRUD operations. */ @@ -159,7 +156,6 @@ var _ = BeforeSuite(func() { err = k8sManager.Start(ctx) Expect(err).ToNot(HaveOccurred(), "failed to run manager") }() - }) /* @@ -177,3 +173,26 @@ var _ = AfterSuite(func() { /* Now that you have your controller running on a test cluster and a client ready to perform operations on your CronJob, we can start writing integration tests! */ + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go index 9c96522aa58..f0fdd40d15d 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -49,6 +49,7 @@ types implement the [Hub](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub) and [Convertible](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible) interfaces, a conversion webhook will be registered. + */ // SetupCronJobWebhookWithManager registers the webhook for CronJob in the manager. @@ -166,7 +167,7 @@ This marker is responsible for generating a validation webhook manifest. // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, // as this struct is used only for temporary operations and does not need to be deeply copied. type CronJobCustomValidator struct { - //TODO(user): Add more fields as needed for validation + // TODO(user): Add more fields as needed for validation } var _ webhook.CustomValidator = &CronJobCustomValidator{} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go index 5ae40bf80a7..b6d3ac3e720 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -164,4 +164,14 @@ var _ = Describe("CronJob Webhook", func() { }) }) + Context("When creating CronJob under Conversion Webhook", func() { + // TODO (user): Add logic to convert the object to the desired version and verify the conversion + // Example: + // It("Should convert the object correctly", func() { + // convertedObj := &batchv1.CronJob{} + // Expect(obj.ConvertTo(convertedObj)).To(Succeed()) + // Expect(convertedObj).ToNot(BeNil()) + // }) + }) + }) diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go index 1b47dd5c702..2f49aa1a62b 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ import ( "crypto/tls" "fmt" "net" + "os" "path/filepath" - "runtime" "testing" "time" @@ -30,10 +30,6 @@ import ( . "github.com/onsi/gomega" admissionv1 "k8s.io/api/admission/v1" - - batchv1 "tutorial.kubebuilder.io/project/api/v1" - - // +kubebuilder:scaffold:imports apimachineryruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -43,16 +39,19 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + + batchv1 "tutorial.kubebuilder.io/project/api/v1" + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var ( - cancel context.CancelFunc - cfg *rest.Config ctx context.Context + cancel context.CancelFunc k8sClient client.Client + cfg *rest.Config testEnv *envtest.Environment ) @@ -67,39 +66,36 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error + scheme := apimachineryruntime.NewScheme() + err = batchv1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + err = admissionv1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: false, - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), - WebhookInstallOptions: envtest.WebhookInstallOptions{ Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, }, } - var err error + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - scheme := apimachineryruntime.NewScheme() - err = batchv1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = admissionv1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) @@ -148,3 +144,26 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook.go index 297e52f89d2..4d7c2963e6c 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -98,7 +98,7 @@ func (d *CronJobCustomDefaulter) Default(ctx context.Context, obj runtime.Object // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods, // as this struct is used only for temporary operations and does not need to be deeply copied. type CronJobCustomValidator struct { - //TODO(user): Add more fields as needed for validation + // TODO(user): Add more fields as needed for validation } var _ webhook.CustomValidator = &CronJobCustomValidator{} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook_test.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook_test.go index 13664e9e0bf..1ee7db018f7 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -84,14 +84,4 @@ var _ = Describe("CronJob Webhook", func() { // }) }) - Context("When creating CronJob under Conversion Webhook", func() { - // TODO (user): Add logic to convert the object to the desired version and verify the conversion - // Example: - // It("Should convert the object correctly", func() { - // convertedObj := &batchv2.CronJob{} - // Expect(obj.ConvertTo(convertedObj)).To(Succeed()) - // Expect(convertedObj).ToNot(BeNil()) - // }) - }) - }) diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/webhook_suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/webhook_suite_test.go index 08aa873ac42..04453d60c48 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/webhook_suite_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/webhook_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ import ( "crypto/tls" "fmt" "net" + "os" "path/filepath" - "runtime" "testing" "time" @@ -30,10 +30,6 @@ import ( . "github.com/onsi/gomega" admissionv1 "k8s.io/api/admission/v1" - - batchv2 "tutorial.kubebuilder.io/project/api/v2" - - // +kubebuilder:scaffold:imports apimachineryruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -43,16 +39,19 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + + batchv2 "tutorial.kubebuilder.io/project/api/v2" + // +kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var ( - cancel context.CancelFunc - cfg *rest.Config ctx context.Context + cancel context.CancelFunc k8sClient client.Client + cfg *rest.Config testEnv *envtest.Environment ) @@ -67,39 +66,36 @@ var _ = BeforeSuite(func() { ctx, cancel = context.WithCancel(context.TODO()) + var err error + scheme := apimachineryruntime.NewScheme() + err = batchv2.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + err = admissionv1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: false, - // The BinaryAssetsDirectory is only required if you want to run the tests directly - // without call the makefile target test. If not informed it will look for the - // default path defined in controller-runtime which is /usr/local/kubebuilder/. - // Note that you must have the required binaries setup under the bin directory to perform - // the tests directly. When we run make test it will be setup and used automatically. - BinaryAssetsDirectory: filepath.Join("..", "..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), - WebhookInstallOptions: envtest.WebhookInstallOptions{ Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, }, } - var err error + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + // cfg is defined in this file globally. cfg, err = testEnv.Start() Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - scheme := apimachineryruntime.NewScheme() - err = batchv2.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = admissionv1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) @@ -148,3 +144,26 @@ var _ = AfterSuite(func() { err := testEnv.Stop() Expect(err).NotTo(HaveOccurred()) }) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_suite_test.go index 265d1829d2c..21174e0b4d1 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_suite_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -60,19 +60,9 @@ var _ = BeforeSuite(func() { By("Ensure that Prometheus is enabled") _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#") - By("generating files") - cmd := exec.Command("make", "generate") - _, err := utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate") - - By("generating manifests") - cmd = exec.Command("make", "manifests") - _, err = utils.Run(cmd) - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests") - By("building the manager(Operator) image") - cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) - _, err = utils.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 diff --git a/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_test.go b/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_test.go index bfc657fb749..94b06366fe2 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_test.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_test.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -46,13 +46,20 @@ var _ = Describe("Manager", Ordered, func() { var controllerPodName string // Before running the tests, set up the environment by creating the namespace, - // installing CRDs, and deploying the controller. + // enforce the restricted security policy to the namespace, installing CRDs, + // and deploying the controller. BeforeAll(func() { By("creating manager namespace") cmd := exec.Command("kubectl", "create", "ns", namespace) _, 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 = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") + By("installing CRDs") cmd = exec.Command("make", "install") _, err = utils.Run(cmd) @@ -93,27 +100,27 @@ var _ = Describe("Manager", Ordered, func() { cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) controllerLogs, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Controller logs:\n %s", controllerLogs)) + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Controller logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err) } By("Fetching Kubernetes events") cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") eventsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Kubernetes events:\n%s", eventsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Kubernetes events: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err) } By("Fetching curl-metrics logs") cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) metricsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Metrics logs:\n %s", metricsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get curl-metrics logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err) } By("Fetching controller manager pod description") @@ -209,10 +216,30 @@ var _ = Describe("Manager", Ordered, func() { By("creating the curl-metrics pod to access the metrics endpoint") cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", "--namespace", namespace, - "--image=curlimages/curl:7.78.0", - "--", "/bin/sh", "-c", fmt.Sprintf( - "curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics", - token, metricsServiceName, namespace)) + "--image=curlimages/curl:latest", + "--overrides", + fmt.Sprintf(`{ + "spec": { + "containers": [{ + "name": "curl", + "image": "curlimages/curl:latest", + "command": ["/bin/sh", "-c"], + "args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"], + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + }], + "serviceAccount": "%s" + } + }`, token, metricsServiceName, namespace, serviceAccountName)) _, err = utils.Run(cmd) Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") @@ -272,6 +299,20 @@ var _ = Describe("Manager", Ordered, func() { Eventually(verifyCAInjection).Should(Succeed()) }) + It("should have CA injection for CronJob conversion webhook", func() { + By("checking CA injection for CronJob conversion webhook") + verifyCAInjection := func(g Gomega) { + cmd := exec.Command("kubectl", "get", + "customresourcedefinitions.apiextensions.k8s.io", + "cronjobs.batch.tutorial.kubebuilder.io", + "-o", "go-template={{ .spec.conversion.webhook.clientConfig.caBundle }}") + vwhOutput, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(len(vwhOutput)).To(BeNumerically(">", 10)) + } + Eventually(verifyCAInjection).Should(Succeed()) + }) + // +kubebuilder:scaffold:e2e-webhooks-checks // TODO: Customize the e2e test suite with scenarios specific to your project. @@ -316,7 +357,7 @@ func serviceAccountToken() (string, error) { // Parse the JSON output to extract the token var token tokenRequest - err = json.Unmarshal([]byte(output), &token) + err = json.Unmarshal(output, &token) g.Expect(err).NotTo(HaveOccurred()) out = token.Status.Token diff --git a/docs/book/src/multiversion-tutorial/testdata/project/test/utils/utils.go b/docs/book/src/multiversion-tutorial/testdata/project/test/utils/utils.go index a239bfd7be2..2367f92fafd 100644 --- a/docs/book/src/multiversion-tutorial/testdata/project/test/utils/utils.go +++ b/docs/book/src/multiversion-tutorial/testdata/project/test/utils/utils.go @@ -1,5 +1,5 @@ /* -Copyright 2024 The Kubernetes authors. +Copyright 2025 The Kubernetes authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -92,7 +92,7 @@ func IsPrometheusCRDsInstalled() bool { if err != nil { return false } - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range prometheusCRDs { for _, line := range crdList { if strings.Contains(line, crd) { @@ -153,7 +153,7 @@ func IsCertManagerCRDsInstalled() bool { } // Check if any of the Cert Manager CRDs are present - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range certManagerCRDs { for _, line := range crdList { if strings.Contains(line, crd) { diff --git a/docs/book/src/multiversion-tutorial/webhooks.md b/docs/book/src/multiversion-tutorial/webhooks.md index 6b383c31c52..ef3283aeafd 100644 --- a/docs/book/src/multiversion-tutorial/webhooks.md +++ b/docs/book/src/multiversion-tutorial/webhooks.md @@ -3,15 +3,6 @@ Our conversion is in place, so all that's left is to tell controller-runtime about our conversion. -Normally, we'd run - -```shell -kubebuilder create webhook --group batch --version v1 --kind CronJob --conversion -``` - -to scaffold out the webhook setup. However, we've already got webhook -setup, from when we built our defaulting and validating webhooks! - ## Webhook setup... {{#literatego ./testdata/project/internal/webhook/v1/cronjob_webhook.go}} diff --git a/docs/book/src/plugins/available/go-v4-plugin.md b/docs/book/src/plugins/available/go-v4-plugin.md index 6e13a9539cc..2ab2e70c6c3 100644 --- a/docs/book/src/plugins/available/go-v4-plugin.md +++ b/docs/book/src/plugins/available/go-v4-plugin.md @@ -44,7 +44,7 @@ kubebuilder init --domain tutorial.kubebuilder.io --repo tutorial.kubebuilder.io [quickstart]: ./../../quick-start.md [testdata]: ./../../../../../testdata [plugins-main]: ./../../../../../cmd/main.go -[kustomize-plugin]: ./../../plugins/avaialable/kustomize-v2.md +[kustomize-plugin]: ./../../plugins/available/kustomize-v2.md [kustomize]: https://github.com/kubernetes-sigs/kustomize [standard-go-project]: https://github.com/golang-standards/project-layout [v4-plugin]: ./../../../../../pkg/plugins/golang/v4 diff --git a/docs/book/src/plugins/available/helm-v1-alpha.md b/docs/book/src/plugins/available/helm-v1-alpha.md new file mode 100644 index 00000000000..4bd25126073 --- /dev/null +++ b/docs/book/src/plugins/available/helm-v1-alpha.md @@ -0,0 +1,92 @@ +# Helm Plugin (`helm/v1-alpha`) + +The Helm plugin is an optional plugin that can be used to scaffold a Helm chart, allowing you to distribute the project using Helm. + +By default, users can generate a bundle with all the manifests by running the following command: + +```bash +make build-installer IMG=/ +``` + +This allows the project consumer to install the solution by applying the bundle with: + +```bash +kubectl apply -f https://raw.githubusercontent.com//project-v4//dist/install.yaml +``` + +However, in many scenarios, you might prefer to provide a Helm chart to package your solution. +If so, you can use this plugin to generate the Helm chart under the `dist` directory. + + + +## When to use it + +- If you want to provide a Helm chart for users to install and manage your project. +- If you need to update the Helm chart generated under `dist/chart/` with the latest project changes: + - After generating new manifests, use the `edit` option to sync the Helm chart. + - **IMPORTANT:** If you have created a webhook or an API using the [DeployImage][deployImage-plugin] plugin, + you must run the `edit` command with the `--force` flag to regenerate the Helm chart values based + on the latest manifests (_after running `make manifests`_) to ensure that the HelmChart values are + updated accordingly. In this case, if you have customized the files + under `dist/chart/values.yaml`, and the `templates/manager/manager.yaml`, you will need to manually reapply your customizations on top + of the latest changes after regenerating the Helm chart. + +## How to use it ? + +### Basic Usage + +The Helm plugin is attached to the `init` subcommand and the `edit` subcommand: + +```sh + +# Initialize a new project with helm chart +kubebuilder init --plugins=helm/v1-alpha + +# Enable or Update the helm chart via the helm plugin to an existing project +# Before run the edit command, run `make manifests` to generate the manifest under `config/` +make manifests +kubebuilder edit --plugins=helm/v1-alpha +``` + + +## Subcommands + +The Helm plugin implements the following subcommands: + +- edit (`$ kubebuilder edit [OPTIONS]`) + +- init (`$ kubebuilder init [OPTIONS]`) + +## Affected files + +The following scaffolds will be created or updated by this plugin: + +- `dist/chart/*` + +[testdata]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata/project-v4-with-plugins +[deployImage-plugin]: ./deploy-image-plugin-v1-alpha.md \ No newline at end of file diff --git a/docs/book/src/plugins/extending/extending_cli_features_and_plugins.md b/docs/book/src/plugins/extending/extending_cli_features_and_plugins.md index 5f794450a1a..26320520997 100644 --- a/docs/book/src/plugins/extending/extending_cli_features_and_plugins.md +++ b/docs/book/src/plugins/extending/extending_cli_features_and_plugins.md @@ -49,9 +49,9 @@ the `create api` and `create webhook` subcommands. Plugins are responsible for implementing the code that will be executed when the sub-commands are called. You can create a new plugin by implementing the [Plugin interface][plugin-interface]. -On top of being a `Base`, a plugin should also implement the [`SubcommandMetadata`][plugin-subc] -interface so it can be run with a CLI. It optionally to set custom help -text for the target command; this method can be a no-op, which will +On top of being a `Base`, a plugin should also implement the [`SubcommandMetadata`][plugin-subc-metadata] +interface so it can be run with a CLI. Optionally, a custom help +text for the target command can be set; this method can be a no-op, which will preserve the default help text set by the [cobra][cobra] command constructors. @@ -80,7 +80,7 @@ There are two ways to specify a plugin to run: - Setting `kubebuilder init --plugins=`, which will initialize a project configured for plugin with key ``. -- A `layout: ` in the scaffolded [PROJECT configuration file][project-file]. Commands (except for `init`, which scaffolds this file) will look at this value before running to choose which plugin to run. +- A `layout: ` in the scaffolded [PROJECT configuration file][project-file-config]. Commands (except for `init`, which scaffolds this file) will look at this value before running to choose which plugin to run. By default, `` will be `go.kubebuilder.io/vX`, where `X` is some integer. @@ -120,10 +120,10 @@ This library allows you to: - Add [markers][markers-scaffold] to the scaffolded files. - Specify templates for your scaffolds. -#### Example: Bollerplate +#### Example: Boilerplate -For instance, the go/v4 scaffolds the `cmd/go.mod` file by defining an object that [implements the machinery interface][machinery]. -The `Template.SetTemplateDefaults`, the `raw template is set to the body: +For instance, the go/v4 scaffolds the `go.mod` file by defining an object that [implements the machinery interface][machinery]. +The raw template is set to the `TemplateBody` field on the `Template.SetTemplateDefaults` method: ```go {{#include ./../../../../../pkg/plugins/golang/v4/scaffolds/internal/templates/gomod.go}} @@ -184,10 +184,10 @@ These utilities allow you to: ### Example If you need to insert custom content into a scaffolded file, -you can use the `Insert` function provided by the plugin utilities: +you can use the `InsertCode` function provided by the plugin utilities: ```go -pluginutil.Insert(file, location, content) +pluginutil.InsertCode(filename, target, code) ``` This approach enables you to extend and modify the generated @@ -394,18 +394,19 @@ creating features or plugins that can rely on this information. [sdk]: https://github.com/operator-framework/operator-sdk [plugin-interface]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin [machinery]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/machinery -[plugin-subc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#Subcommand +[plugin-subc-metadata]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#SubcommandMetadata [plugin-version-type]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#Version [bundle-plugin-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#Bundle [deprecate-plugin-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#Deprecated [plugin-sub-command]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#Subcommand [plugin-update-meta]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin#UpdatesMetadata +[plugin-utils]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin/util [markers-scaffold]: ./../../reference/markers/scaffold.md -[kb-utils]: ./../../../../../pkg/plugin/util/util.go +[kb-utils]: https://github.com/kubernetes-sigs/kubebuilder/blob/book-v4/pkg/plugin/util/util.go [project-file-config]: ./../../reference/project-config.md -[cli]: ./../../../../../pkg/cli -[kb-go-plugin]: ./../../../../../pkg/plugins/golang/v4 +[cli]: https://github.com/kubernetes-sigs/kubebuilder/tree/book-v4/pkg/cli +[kb-go-plugin]: https://github.com/kubernetes-sigs/kubebuilder/tree/book-v4/pkg/plugins/golang/v4 [cobra]: https://github.com/spf13/cobra [external-plugin]: external-plugins.md [deploy-image]: ./../available/deploy-image-plugin-v1-alpha.md -[upgrade-assistant]: ./../../reference/rescaffold.md \ No newline at end of file +[upgrade-assistant]: ./../../reference/rescaffold.md diff --git a/docs/book/src/plugins/extending/external-plugins.md b/docs/book/src/plugins/extending/external-plugins.md index 03c344037f5..a957f88fcc8 100644 --- a/docs/book/src/plugins/extending/external-plugins.md +++ b/docs/book/src/plugins/extending/external-plugins.md @@ -17,7 +17,7 @@ manage any changes within their domain of responsibility. If you are interested in this type of integration, collaborating with the maintainers of the third-party solution is recommended. Kubebuilder's maintainers -is always willing to provide support in extending its capabilities. +are always willing to provide support in extending its capabilities. ## How to Write an External Plugin @@ -117,7 +117,7 @@ Otherwise, Kubebuilder would search for the plugins in a default path based on y ### Example CLI Commands -Now, you can using it by calling the CLI commands: +You can now use it by calling the CLI commands: ```sh # Initialize a new project with the external plugin named `sampleplugin` @@ -148,4 +148,4 @@ kubebuilder create api --plugins go/v4,sampleplugin/v1 - A [sample external plugin written in Python](https://github.com/rashmigottipati/POC-Phase2-Plugins) - A [sample external plugin written in JavaScript](https://github.com/Eileen-Yu/kb-js-plugin) -[code-plugin-external]: ./../../../../../pkg/plugin/external/types.go +[code-plugin-external]: https://github.com/kubernetes-sigs/kubebuilder/blob/book-v4/pkg/plugin/external/types.go diff --git a/docs/book/src/plugins/extending/testing-plugins.md b/docs/book/src/plugins/extending/testing-plugins.md index f58121a60bd..6482bcdc5ed 100644 --- a/docs/book/src/plugins/extending/testing-plugins.md +++ b/docs/book/src/plugins/extending/testing-plugins.md @@ -55,7 +55,7 @@ Here’s a general workflow to create a sample project using the `go/v4` plugin "--domain", kbc.Domain, "--fetch-deps=false", ) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + Expect(err).NotTo(HaveOccurred(), "Failed to initialize a project") ``` - **To define API:** @@ -70,7 +70,7 @@ Here’s a general workflow to create a sample project using the `go/v4` plugin "--controller", "--make=false", ) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + Expect(err).NotTo(HaveOccurred(), "Failed to create an API") ``` - **To scaffold webhook configurations:** @@ -83,26 +83,26 @@ Here’s a general workflow to create a sample project using the `go/v4` plugin "--defaulting", "--programmatic-validation", ) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) + Expect(err).NotTo(HaveOccurred(), "Failed to create an webhook") ``` -[sdk-e2e-tests]: https://github.com/operator-framework/operator-sdk/tree/master/test/e2e/go -[new-context]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#NewTestContext -[kubectl-ktc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#Kubectl -[prepare-method]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Prepare [cert-manager-install]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.InstallCertManager -[prometheus-manager-install]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.InstallPrometheusOperManager -[init-subcommand]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Init [create-api-subcommand]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.CreateAPI -[plugin-util]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin/util +[destroy-method]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Destroy +[extending-cli]: ./extending_cli_features_and_plugins.md +[init-subcommand]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Init [insert-code]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin/util#InsertCode +[kb-e2e-tests]: https://github.com/kubernetes-sigs/kubebuilder/tree/book-v4/test/e2e +[kb-samples]: https://github.com/kubernetes-sigs/kubebuilder/tree/book-v4/testdata +[kubectl-ktc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#Kubectl +[load-image-to-kind]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.LoadImageToKindCluster +[make-command]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Make +[new-context]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#NewTestContext +[plugin-util]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin/util +[prepare-method]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Prepare +[prometheus-manager-install]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.InstallPrometheusOperManager [replace-in-file]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin/util#ReplaceInFile +[sdk-e2e-tests]: https://github.com/operator-framework/operator-sdk/tree/master/test/e2e/go [uncomment-code]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/plugin/util#UncommentCode -[make-command]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Make -[load-image-to-kind]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.LoadImageToKindCluster [uninstall-prometheus-manager]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.UninstallPrometheusOperManager -[destroy-method]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.Destroy -[kb-samples]: ./../../../../../testdata -[kb-e2e-tests]: ./../../../../../test/e2e/ -[utils-kb]: ./../../../../../test/e2e/utils -[extending-cli]: ./extending_cli_features_and_plugins.md +[utils-kb]: https://github.com/kubernetes-sigs/kubebuilder/tree/book-v4/test/e2e/utils diff --git a/docs/book/src/plugins/to-add-optional-features.md b/docs/book/src/plugins/to-add-optional-features.md index 6b95e7c02c2..f6e847672dd 100644 --- a/docs/book/src/plugins/to-add-optional-features.md +++ b/docs/book/src/plugins/to-add-optional-features.md @@ -2,10 +2,12 @@ The following plugins are useful to generate code and take advantage of optional features -| Plugin | Key | Description | -|---------------------------------------------------| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [grafana.kubebuilder.io/v1-alpha][grafana] | `grafana/v1-alpha` | Optional helper plugin which can be used to scaffold Grafana Manifests Dashboards for the default metrics which are exported by controller-runtime. | -| [deploy-image.go.kubebuilder.io/v1-alpha][deploy] | `deploy-image/v1-alpha` | Optional helper plugin which can be used to scaffold APIs and controller with code implementation to Deploy and Manage an Operand(image). | +| Plugin | Key | Description | +|---------------------------------------------------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| [grafana.kubebuilder.io/v1-alpha][grafana] | `grafana/v1-alpha` | Optional helper plugin which can be used to scaffold Grafana Manifests Dashboards for the default metrics which are exported by controller-runtime. | +| [deploy-image.go.kubebuilder.io/v1-alpha][deploy] | `deploy-image/v1-alpha` | Optional helper plugin which can be used to scaffold APIs and controller with code implementation to Deploy and Manage an Operand(image). | +| [helm.kubebuilder.io/v1-alpha][helm] | `helm/v1-alpha` | Optional helper plugin which can be used to scaffold a Helm Chart to distribute the project under the `dist` directory | [grafana]: ./available/grafana-v1-alpha.md -[deploy]: ./available/deploy-image-plugin-v1-alpha.md \ No newline at end of file +[deploy]: ./available/deploy-image-plugin-v1-alpha.md +[helm]: ./available/helm-v1-alpha.md \ No newline at end of file diff --git a/docs/book/src/quick-start.md b/docs/book/src/quick-start.md index 137b951de0a..98c1fa458e7 100644 --- a/docs/book/src/quick-start.md +++ b/docs/book/src/quick-start.md @@ -9,7 +9,7 @@ This Quick Start guide will cover: ## Prerequisites -- [go](https://golang.org/dl/) version v1.22.0+ +- [go](https://go.dev/dl/) version v1.23.0+ - [docker](https://docs.docker.com/install/) version 17.03+. - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) version v1.11.3+. - Access to a Kubernetes v1.11.3+ cluster. @@ -243,8 +243,8 @@ understanding by developing a demo project. [pre-rbc-gke]: https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control#iam-rolebinding-bootstrap [cronjob-tutorial]: https://book.kubebuilder.io/cronjob-tutorial/cronjob-tutorial.html -[GOPATH-golang-docs]: https://golang.org/doc/code.html#GOPATH -[go-modules-blogpost]: https://blog.golang.org/using-go-modules +[GOPATH-golang-docs]: https://go.dev/doc/code.html#GOPATH +[go-modules-blogpost]: https://blog.go.dev/using-go-modules [envtest]: https://book.kubebuilder.io/reference/testing/envtest.html [architecture-concept-diagram]: architecture.md [kustomize]: https://github.com/kubernetes-sigs/kustomize diff --git a/docs/book/src/reference/markers/scaffold.md b/docs/book/src/reference/markers/scaffold.md index 48d18fa88bf..ab68f4f02db 100644 --- a/docs/book/src/reference/markers/scaffold.md +++ b/docs/book/src/reference/markers/scaffold.md @@ -103,10 +103,67 @@ properly registered with the manager, so that the controller can reconcile the r | `+kubebuilder:scaffold:webhook` | `webhooks suite tests` files | Marks where webhook setup functions are added. | | `+kubebuilder:scaffold:crdkustomizeresource`| `config/crd` | Marks where CRD custom resource patches are added. | | `+kubebuilder:scaffold:crdkustomizewebhookpatch` | `config/crd` | Marks where CRD webhook patches are added. | -| `+kubebuilder:scaffold:crdkustomizecainjectionpatch` | `config/crd` | Marks where CA injection patches are added for the webhook. | +| `+kubebuilder:scaffold:crdkustomizecainjectionns` | `config/default` | Marks where CA injection patches are added for the conversion webhooks. | +| `+kubebuilder:scaffold:crdkustomizecainjectioname` | `config/default` | Marks where CA injection patches are added for the conversion webhooks. | +| **(No longer supported)** `+kubebuilder:scaffold:crdkustomizecainjectionpatch` | `config/crd` | Marks where CA injection patches are added for the webhooks. Replaced by `+kubebuilder:scaffold:crdkustomizecainjectionns` and `+kubebuilder:scaffold:crdkustomizecainjectioname` | | `+kubebuilder:scaffold:manifestskustomizesamples` | `config/samples` | Marks where Kustomize sample manifests are injected. | | `+kubebuilder:scaffold:e2e-webhooks-checks` | `test/e2e` | Adds e2e checks for webhooks depending on the types of webhooks scaffolded. | + +