diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index 2fb6815d07f..6bc3edb3f32 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -1,5 +1,5 @@
name: Bug Report
-about: Problems and issues with code or docs
+description: Problems and issues with code or docs
labels:
- kind/bug
body:
@@ -18,7 +18,7 @@ body:
cross-repository effort, it probably belongs here. Feel free to continue :wink:
[cr-issue]: https://github.com/kubernetes-sigs/controller-runtime/issues/new
- [cr-issue]: https://github.com/kubernetes-sigs/controller-tools/issues/new
+ [ct-issue]: https://github.com/kubernetes-sigs/controller-tools/issues/new
- type: markdown
attributes:
@@ -36,7 +36,7 @@ body:
Code & details examples
`````markdown
- Some code code written in Go:
+ Some code written in Go:
```go
type Manager struct {
@@ -98,7 +98,7 @@ body:
id: cli-version
attributes:
label: KubeBuilder (CLI) Version
- description: "use `kubebuilder --version` to find this out"
+ description: "use `kubebuilder version` to find this out"
validations:
required: true
@@ -126,7 +126,7 @@ body:
- type: dropdown
attributes:
- label: " "
+ label: "Extra Labels"
description: |
If this is *also* a documentation request, etc, please select that below.
multiple: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 334bc239e2a..18121c08a39 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,5 @@
# allow free form issues as an escape hatch. This can be taken away if people abuse it ;-)
-blank_issues_enabled: true
+blank_issues_enabled: true
# link to CR and CT for easier access
contact_links:
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml
index dd75da985ab..e0385693029 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yaml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yaml
@@ -18,13 +18,13 @@ body:
cross-repository effort, it probably belongs here. Feel free to continue :wink:
[cr-issue]: https://github.com/kubernetes-sigs/controller-runtime/issues/new
- [cr-issue]: https://github.com/kubernetes-sigs/controller-tools/issues/new
+ [ct-issue]: https://github.com/kubernetes-sigs/controller-tools/issues/new
- type: markdown
attributes:
value: |
# Hiya! Welcome to Kubebuilder!
-
+
For a smooth issue process, try to answer the following questions.
Don't worry if they're not all applicable; just try to include what you can :smile:
@@ -34,9 +34,9 @@ body:
Code & details examples
-
+
`````markdown
- Some code code written in Go:
+ Some code written in Go:
```go
type Manager struct {
@@ -63,7 +63,7 @@ body:
`````
- [mdn-details]: ://developer.mozilla.org/en-US/docs/Web/HTML/Element/details
+ [mdn-details]: ://developer.mozilla.org/en-US/docs/Web/HTML/Element/details
- type: textarea
attributes:
@@ -75,13 +75,13 @@ body:
Does it require a particular Kubernetes version?
- Is there currently another isssue associated with this (use github syntax
+ Is there currently another issue associated with this (use github syntax
like `#xyz` to link to it)?
validations: {required: true}
- type: dropdown
- attributes:
- label: " "
+ attributes:
+ label: "Extra Labels"
description: |
If this is *also* a documentation request, etc, please select that below.
multiple: true
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 4225ad23967..151f7bca297 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,6 @@
+
+## Open Questions [optional]
+
+
+
+## Summary
+
+
+
+## Motivation
+
+
+
+### Goals
+
+
+
+### Non-Goals
+
+
+
+## Proposal
+
+
+
+### User Stories
+
+
+
+### Implementation Details/Notes/Constraints [optional]
+
+
+
+### Risks and Mitigations
+
+
+
+### Proof of Concept [optional]
+
+
+
+## Drawbacks
+
+
+
+## Alternatives
+
+
diff --git a/designs/update_action.md b/designs/update_action.md
new file mode 100644
index 00000000000..40f5c9572c9
--- /dev/null
+++ b/designs/update_action.md
@@ -0,0 +1,547 @@
+| Authors | Creation Date | Status | Extra |
+|-----------------|---------------|-------------|-------|
+| @camilamacedo86 | 2024-11-07 | Implementable | - |
+| @vitorfloriano | | Implementable | - |
+
+# Proposal: Automating Operator Maintenance: Driving Better Results with Less Overhead
+
+## Introduction
+
+Code-generation tools like **Kubebuilder** and **Operator-SDK** have revolutionized cloud-native application development by providing scalable, community-driven frameworks. These tools simplify complexity, accelerate development, and enable developers to create tailored solutions while avoiding common pitfalls, establishing a strong foundation for innovation.
+
+However, as these tools evolve to keep up with ecosystem changes and new features, projects risk becoming outdated. Manual updates are time-consuming, error-prone, and create challenges in maintaining security, adopting advancements, and staying aligned with modern standards.
+
+This project proposes an **automated solution for Kubebuilder**, with potential applications for similar tools or those built on its foundation. By streamlining maintenance, projects remain modern, secure, and adaptable, fostering growth and innovation across the ecosystem. The automation lets developers focus on what matters most: **building great solutions**.
+
+
+## Problem Statement
+
+Kubebuilder is widely used for developing Kubernetes operators, providing a standardized scaffold. However, as the ecosystem evolves, keeping projects up-to-date presents challenges due to:
+
+- **Manual re-scaffolding processes**: These are time-intensive and error-prone.
+- **Increased risk of outdated configurations**: Leads to security vulnerabilities and incompatibility with modern practices.
+
+## Proposed Solution
+
+This proposal introduces a **workflow-based tool** (such as a GitHub Action) that automates updates for Kubebuilder projects. Whenever a new version of Kubebuilder is released, the tool initiates a workflow that:
+
+1. **Detects the new release**.
+2. **Generates an updated scaffold**.
+3. **Performs a three-way merge to retain customizations**.
+4. **Creates a pull request (PR) summarizing the updates** for review and merging.
+
+## Example Usage
+
+### GitHub Actions Workflow:
+
+1. A user creates a project with Kubebuilder `v4.4.3`.
+2. When Kubebuilder `v4.5.0` is released, a **pull request** is automatically created.
+3. The PR includes scaffold updates while preserving the user’s customizations, allowing easy review and merging.
+
+### Local Tool Usage:
+
+1. A user creates a project with Kubebuilder `v4.4.3`
+2. When Kubebuilder `v4.5.0` is released, they run `kubebuilder alpha update` which calls `kubebuilder alpha generate` behind the scenes
+3. The tool updates the scaffold and preserves customizations for review and application.
+4. In case of conflicts, the tool allows users to resolve them before push a pull request with the changes.
+
+### Handling Merge Conflicts
+
+**Local Tool Usage**:
+
+If conflicts cannot be resolved automatically, developers can manually address
+them before completing the update.
+
+**GitHub Actions Workflow**:
+
+If conflicts arise during the merge, the action will create a pull request and
+the conflicst will be highlighted in the PR. Developers can then review and resolve
+them. The PR will contains the default markers:
+
+**Example**
+
+```go
+<<<<<<< HEAD
+ _ = logf.FromContext(ctx)
+=======
+log := log.FromContext(ctx)
+>>>>>>> original
+```
+
+## Open Questions
+
+### 1. Do we need to create branches to perform the three-way merge,or can we use local temporary directories?
+
+> While temporary directories are sufficient for simple three-way merges, branches are better suited for complex scenarios.
+> They provide history tracking, support collaboration, integrate with CI/CD workflows, and offer more advanced
+> conflict resolution through Git’s merge command. For these reasons, it seems more appropriate to use branches to ensure
+> flexibility and maintainability in the merging process.
+
+> Furthermore, branches allows a better resolution strategy,
+> since allows us to use `kubebuilder alpha generate` command to-rescaffold the projects
+> using the same name directory and provide a better history for the PRs
+> allowing users to see the changes and have better insights for conflicts
+> resolution.
+
+### 2. What Git configuration options can facilitate the three-way merge?
+
+Several Git configuration options can improve the three-way merge process:
+
+```bash
+# Show all three versions (base, current, and updated) during conflicts
+git config --global merge.conflictStyle diff3
+
+# Enable "reuse recorded resolution" to remember and reuse previous conflict resolutions
+git config --global rerere.enabled true
+
+# Increase the rename detection limit to better handle renamed or moved files
+git config --global merge.renameLimit 999999
+```
+
+These configurations enhance the merging process by improving conflict visibility,
+reusing resolutions, and providing better file handling, making three-way
+merges more efficient and developer-friendly.
+
+### 3. If we change Git configurations, can we isolate these changes to avoid affecting the local developer environment when the tool runs locally?
+
+It seems that changes can be made using the `-c` flag, which applies the
+configuration only for the duration of a specific Git command. This ensures
+that the local developer environment remains unaffected.
+
+For example:
+
+```
+git -c merge.conflictStyle=diff3 -c rerere.enabled=true merge
+```
+
+### 4. How can we minimize and resolve conflicts effectively during merges?
+
+- **Enable Git Features:**
+ - Use `git config --global rerere.enabled true` to reuse previous conflict resolutions.
+ - Configure custom merge drivers for specific file types (e.g., `git config --global merge.<driver>.name "Custom Merge Driver"`).
+
+- **Encourage Standardization:**
+ - Adopt a standardized scaffold layout to minimize divergence and reduce conflicts.
+
+- **Apply Frequent Updates:**
+ - Regularly update projects to avoid significant drift between the scaffold and customizations.
+
+These strategies help minimize conflicts and simplify their resolution during merges.
+
+### 5. How to create the PR with the changes for projects that are monorepos?
+That means the result of Kubebuilder is not defined in the root dir and might be in other paths.
+
+We can define an `--output` directory and a configuration for the GitHub Action where
+users will define where in their repo the path for the Kubebuilder project is.
+However, this might be out of scope for the initial version.
+
+### 6. How could AI help us solve conflicts? Are there any available solutions?
+
+While AI tools like GitHub Copilot can assist in code generation and provide suggestions,
+however, it might be risky be 100% dependent on AI for conflict resolution, especially in complex scenarios.
+Therefore, we might want to use AI as a complementary tool rather than a primary solution.
+
+AI can help by:
+- Providing suggestions for resolving conflicts based on context.
+- Analyzing code patterns to suggest potential resolutions.
+- Offering explanations for conflicts and suggesting best practices.
+- Assisting in summarizing changes.
+
+## Summary
+
+### Workflow Example:
+
+1. A developer creates a project with Kubebuilder `v4.4`.
+2. The tooling uses the release of Kubebuilder `v4.5`.
+3. The tool:
+ - Regenerates the original base source code for `v4.4` using the `clientVersion` in the `PROJECT` file.
+ - Generates the base source code for `v4.5`
+4. A three-way merge integrates the changes into the developer’s project while retaining custom code.
+5. The changes now can be packaged into a pull request, summarizing updates and conflicts for the developer’s review.
+
+### Steps:
+
+The proposed implementation involves the following steps:
+
+1. **Version Tracking**:
+ - Record the `clientVersion` (initial Kubebuilder version) in the `PROJECT` file.
+ - Use this version as a baseline for updates.
+ - Available in the `PROJECT` file, from [v4.6.0](https://github.com/kubernetes-sigs/kubebuilder/releases/tag/v4.6.0) release onwards.
+
+2. **Scaffold Generation**:
+ - Generate the **original scaffold** using the recorded version.
+ - Generate the **updated scaffold** using the latest Kubebuilder release.
+
+3. **Three-Way Merge**:
+ - Ensure git is configured to handle three-way merges.
+ - Merge the original scaffold, updated scaffold, and the user’s customized project.
+ - Preserve custom code during the merge.
+
+4. **(For Actions) - Pull Request Creation**:
+ - Open a pull request summarizing changes, including details on conflict resolution.
+ - Schedule updates weekly or provide an on-demand option.
+
+#### Example Workflow
+
+The following example code illustrates the proposed idea but has not been evaluated.
+This is an early, incomplete draft intended to demonstrate the approach and basic concept.
+
+We may want to develop a dedicated command-line tool, such as `kubebuilder alpha update`,
+to handle tasks like downloading binaries, merging, and updating the scaffold. In this approach,
+the GitHub Action would simply invoke this tool to manage the update process and open the
+Pull Request, rather than performing each step directly within the Action itself.
+
+```yaml
+name: Workflow Auto-Update
+
+permissions:
+ contents: write
+ pull-requests: write
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 0 * * 1" # Every Monday 00:00 UTC
+
+jobs:
+ alpha-update:
+ runs-on: ubuntu-latest
+
+ steps:
+ # 1) Checkout the repository with full history
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ fetch-depth: 0
+
+ # 2) Install the latest stable Go toolchain
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: 'stable'
+
+ # 3) Install Kubebuilder CLI
+ - name: Install Kubebuilder
+ run: |
+ curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
+ chmod +x kubebuilder
+ sudo mv kubebuilder /usr/local/bin/
+
+ # 4) Extract Kubebuilder version (e.g., v4.6.0) for branch/title/body
+ - name: Get Kubebuilder version
+ id: kb
+ shell: bash
+ run: |
+ RAW="$(kubebuilder version 2>/dev/null || true)"
+ VERSION="$(printf "%s" "$RAW" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1)"
+ echo "version=${VERSION:-vunknown}" >> "$GITHUB_OUTPUT"
+
+ # 5) Run kubebuilder alpha update
+ - name: Run kubebuilder alpha update
+ run: |
+ kubebuilder alpha update --force
+
+ # 6) Restore workflow files so the update doesn't overwrite CI config
+ - name: Restore workflows directory
+ run: |
+ git restore --source=main --staged --worktree .github/workflows
+ git add .github/workflows
+ git commit --amend --no-edit || true
+
+ # 7) Push to a versioned branch; create PR if missing, otherwise it just updates
+ - name: Push branch and create/update PR
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ shell: bash
+ run: |
+ set -euo pipefail
+ VERSION="${{ steps.kb.outputs.version }}"
+ PR_BRANCH="kubebuilder-update-to-${VERSION}"
+
+ # Create or update the branch and push
+ git checkout -B "$PR_BRANCH"
+ git push -u origin "$PR_BRANCH" --force
+
+ PR_TITLE="chore: update scaffolding to Kubebuilder ${VERSION}"
+ PR_BODY=$'Automated update of Kubebuilder project scaffolding to '"${VERSION}"$'.\n\nMore info: https://github.com/kubernetes-sigs/kubebuilder/releases\n\n :warning: If conflicts arise, resolve them and run:\n```bash\nmake manifests generate fmt vet lint-fix\n```'
+
+ # Try to create the PR; ignore error only if it already exists
+ if ! gh pr create \
+ --title "${PR_TITLE}" \
+ --body "${PR_BODY}" \
+ --base main \
+ --head "$PR_BRANCH"
+ then
+ EXISTING="$(gh pr list --state open --head "$PR_BRANCH" --json number --jq '.[0].number' || true)"
+ if [ -n "${EXISTING}" ]; then
+ echo "PR #${EXISTING} already exists for ${PR_BRANCH}, branch updated."
+ else
+ echo "Failed to create PR for ${PR_BRANCH} and no open PR found."
+ exit 1
+ fi
+ fi
+```
+
+## Motivation
+
+A significant challenge faced by Kubebuilder users is keeping their projects up-to-date with the latest
+scaffolds while preserving customizations. The manual processes required for updates are time-consuming,
+error-prone, and often discourage users from adopting new versions, leading to outdated and insecure projects.
+
+The primary motivation for this proposal is to simplify and automate the process of maintaining Kubebuilder
+projects. By providing a streamlined workflow for updates, this solution ensures that users can keep
+their projects aligned with modern standards while retaining their customizations.
+
+### Goals
+
+- **Automate Updates**: Detect and apply scaffold updates while preserving customizations.
+- **Simplify Updates**: Generate pull requests for easy review and merging.
+- **Provide Local Tooling**: Allow developers to run updates locally with preserved customizations.
+- **Keep Projects Current**: Ensure alignment with the latest scaffold improvements.
+- **Minimize Disruptions**: Enable scheduled or on-demand updates.
+
+### Non-Goals
+
+- **Automating conflict resolution for heavily customized projects**.
+- **Automatically merging updates without developer review**.
+- **Supporting monorepo project layouts or handling repositories that contain more than just the Kubebuilder-generated code**.
+
+## Proposal
+
+### User Stories
+
+- **As a Kubebuilder maintainer**, I want to help users keep their projects updated with minimal effort, ensuring they adhere to best practices and maintain alignment with project standards.
+- **As a user of Kubebuilder**, I want my project to stay up-to-date with the latest scaffold best practices while preserving customizations.
+- **As a user of Kubebuilder**, I want an easy way to apply updates across multiple repositories, saving time on manual updates.
+- **As a user of Kubebuilder**, I want to ensure my codebases remain secure and maintainable without excessive manual effort.
+
+### Implementation Details/Notes/Constraints
+
+- Introduce a new [Kubebuilder Plugin](https://book.kubebuilder.io/plugins/plugins) that scaffolds the
+ **GitHub Action** based on the POC. This plugin will be released as an **alpha feature**,
+ allowing users to opt-in for automated updates.
+
+- The plugin should be added by default in the Golang projects build with Kubebuilder, so new
+ projects can benefit from the automated updates without additional configuration. While it will not be escaffolded
+ by default in tools which extend Kubebuilder such as the Operator-SDK, where the alpha generate and update
+ features cannot be ported or extended.
+
+- Documentation should be provided to guide users on how to enable and use the new plugin as the new alpha command
+
+- The alpha command update should
+ - provide help and examples of usage
+ - allow users to specify the version of Kubebuilder they want to update to or from to
+ - allow users to specify the path of the project they want to update
+ - allow users to specify the output directory where the updated scaffold should be generated
+ - re-use the existing `kubebuilder alpha generate` command to generate the updated scaffold
+
+- The `kubebuilder alpha update` command should be covered with e2e tests to ensure it works as expected
+ and that the generated scaffold is valid and can be built.
+
+## Risks and Mitigations
+- **Risk**: Frequent conflicts may make the process cumbersome.
+ - *Mitigation*: Provide clear conflict summaries and leverage GitHub preview tools.
+- **Risk**: High maintenance overhead.
+ - *Mitigation*: Build a dedicated command-line tool (`kubebuilder alpha update`) to streamline updates and minimize complexity.
+
+## Proof of Concept
+
+The feasibility of re-scaffolding projects has been demonstrated by the
+`kubebuilder alpha generate` command.
+
+**Command Example:**
+
+```bash
+kubebuilder alpha generate
+```
+
+For more details, refer to the [Alpha Generate Documentation](https://kubebuilder.io/reference/rescaffold).
+
+This command allows users to manually re-scaffold a project, to allow users add their code on top.
+It confirms the technical capability of regenerating and updating scaffolds effectively.
+
+This proposal builds upon this foundation by automating the process. The proposed tool would extend this functionality
+to automatically update projects with new scaffold versions, preserving customizations.
+
+The three-way merge approach is a common strategy for integrating changes from multiple sources.
+It is widely used in version control systems to combine changes from a common ancestor with two sets of modifications.
+In the context of this proposal, the three-way merge would combine the original scaffold, the updated scaffold, and the user’s custom code
+seems to be very promising.
+
+### POC Implementation using 3-way merge:
+
+Following some POCs done to demonstrate the three-way merge approach
+where a project was escaffolded with Kubebuilder `v4.5.0` or `v4.5.2`
+and then updated to `v4.6.0`
+
+```shell
+## The following options were passed when merging UPGRADE:
+
+git config --global merge.yaml.name "Custom YAML merge"
+git config --global merge.yaml.driver "yaml-merge %O %A %B"
+git config merge.conflictStyle diff3
+git config rerere.enabled true
+git config merge.renameLimit 999999
+Here are the steps taken:
+
+## On main:
+
+git checkout -b ancestor
+Clean up the ancestor and commit
+
+rm -fr *
+git add .
+git commit -m "clean up ancestor"
+
+## Bring back the PROJECT file, re-scaffold with v4.5.0, and commit
+
+git checkout main -- PROJECT
+kubebuilder alpha generate
+git add .
+git commit -m "alpha generate on ancestor with 4.5.0"
+## Then proceed to create the original (ours) branch, bring back the code on main, add and commit:
+
+git checkout -b original
+git checkout main -- .
+git add .
+git commit -m "add code back in original"
+
+## Then create the upgrade branch (theirs), run kubebuilder alpha generate with v4.6.0 add and commit:
+
+git checkout ancestor
+git checkout -b upgrade
+kubebuilder alpha generate
+git add .
+git commit -m "alpha generate on upgrade with 4.6.0"
+
+## So now we have the ancestor, the original, and the upgrade branches all set, we can create a branch to commit the merge with the conflict markers:
+
+git checkout original
+git checkout -b merge
+git merge upgrade
+git add .
+git commit -m "Merge with upgrade with conflict markers"
+## Now that we have performed the three way merge and commited the conflict markers, we can open a PR against main.
+```
+
+As the script:
+
+```bash
+#!/bin/bash
+
+set -euo pipefail
+
+# CONFIG — change as needed
+REPO_PATH="$HOME/go/src/github/camilamacedo86/wordpress-operator"
+KUBEBUILDER_SRC="$HOME/go/src/sigs.k8s.io/kubebuilder"
+PROJECT_FILE="PROJECT"
+
+echo "📦 Kubebuilder 3-way merge upgrade (v4.5.0 → v4.6.0)"
+echo "📂 Working in: $REPO_PATH"
+echo "🧪 Kubebuilder source: $KUBEBUILDER_SRC"
+
+cd "$REPO_PATH"
+
+# Step 1: Create ancestor branch and clean it up
+echo "🌱 Creating 'ancestor' branch"
+git checkout -b ancestor main
+
+echo "🧼 Cleaning all files and folders (including dotfiles), except .git and PROJECT"
+find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'PROJECT' -exec rm -rf {} +
+
+git add -A
+git commit -m "Clean ancestor branch"
+
+# Step 2: Install Kubebuilder v4.5.0 and regenerate scaffold
+echo "⬇️ Installing Kubebuilder v4.5.0"
+cd "$KUBEBUILDER_SRC"
+git checkout upstream/release-4.5
+make install
+kubebuilder version
+
+cd "$REPO_PATH"
+echo "📂 Restoring PROJECT file"
+git checkout main -- "$PROJECT_FILE"
+kubebuilder alpha generate
+make manifests generate fmt vet lint-fix
+git add -A
+git commit -m "alpha generate on ancestor with v4.5.0"
+
+# Step 3: Create original branch with user's code
+echo "📦 Creating 'original' branch with user code"
+git checkout -b original
+git checkout main -- .
+git add -A
+git commit -m "Add project code into original"
+
+# Step 4: Install Kubebuilder v4.6.0 and scaffold upgrade
+echo "⬆️ Installing Kubebuilder v4.6.0"
+cd "$KUBEBUILDER_SRC"
+git checkout upstream/release-4.6
+make install
+kubebuilder version
+
+cd "$REPO_PATH"
+echo "🌿 Creating 'upgrade' branch from ancestor"
+git checkout ancestor
+git checkout -b upgrade
+echo "🧼 Cleaning all files and folders (including dotfiles), except .git and PROJECT"
+find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'PROJECT' -exec rm -rf {} +
+
+kubebuilder alpha generate
+make manifests generate fmt vet lint-fix
+git add -A
+git commit -m "alpha generate on upgrade with v4.6.0"
+
+# Step 5: Merge original into upgrade and preserve conflicts
+echo "🔀 Creating 'merge' branch from upgrade and merging original"
+git checkout upgrade
+git checkout -b merge
+
+# Do a non-interactive merge and commit manually
+echo "🤖 Running non-interactive merge..."
+set +e
+git merge --no-edit --no-commit original
+MERGE_EXIT_CODE=$?
+set -e
+
+# Stage everything and commit with an appropriate message
+if [ $MERGE_EXIT_CODE -ne 0 ]; then
+ # Manually the alpha generate should out put the info so the person can fix it
+ echo "⚠️ Conflicts occurred."
+ echo "You will need to fix the conflicts manually and run the following commands:"
+ echo "make manifests generate fmt vet lint-fix"
+ echo "⚠️ Conflicts occurred. Keeping conflict markers and committing them."
+ git add -A
+ git commit -m "upgrade has conflicts to be solved"
+else
+ echo "Merge successful with no conflicts. Running commands"
+ make manifests generate fmt vet lint-fix
+
+ echo "✅ Merge successful with no conflicts."
+ git add -A
+ git commit -m "upgrade worked without conflicts"
+fi
+
+echo ""
+echo "📍 You are now on the 'merge' branch."
+echo "📤 Push with: git push -u origin merge"
+echo "🔁 Then open a PR to 'main' on GitHub."
+echo ""
+```
+
+## Drawbacks
+
+- **Frequent Conflicts:** Automated updates may often result in conflicts, making the process cumbersome for users.
+- **Complex Resolutions:** If conflicts are hard to review and resolve, users may find the solution impractical.
+- **Maintenance Overhead:** The implementation could become too complex for maintainers to develop and support effectively.
+
+## Alternatives
+
+- **Manual Update Workflow**: Continue with manual updates where users regenerate
+and merge changes independently, though this is time-consuming and error-prone.
+- **Use alpha generate command**: Continue with partially automated updates provided
+by the alpha generate command.
+- **Dependabot Integration**: Leverage Dependabot for dependency updates, though this
+doesn’t fully support scaffold updates and could lead to incomplete upgrades.
diff --git a/doc.go b/doc.go
deleted file mode 100644
index f9a0cc4d13f..00000000000
--- a/doc.go
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-//go:generate go run github.com/markbates/pkger/cmd/pkger
-
-// Package kubebuilder contains pkged files compiled into the
-// go binaries.
-package kubebuilder
diff --git a/docs/CONTRIBUTING-ROLES.md b/docs/CONTRIBUTING-ROLES.md
index eb92536f51f..76d48acdf75 100644
--- a/docs/CONTRIBUTING-ROLES.md
+++ b/docs/CONTRIBUTING-ROLES.md
@@ -4,7 +4,7 @@ Contributing Roles
## Direct Code-Related Roles
While anyone (who's signed the [CLA and follows the code of
-conduct](../CONTRIBUTING.md)) is welcome to contribute to the KubeBuilder
+conduct](../CONTRIBUTING.md)) is welcome to contribute to the Kubebuilder
project, we've got two "formal" roles that carry additional privileges and
responsibilities: *reviewer* and *approver*.
@@ -14,7 +14,7 @@ project, or the project as a whole. We follow a similar set of
definitions to the [main Kubernetes project itself][kube-ladder], with
slightly looser requirements.
-As much as possible, we want people to help take on responsibility in the
+As much as possible, we want people to help take on responsibility for the
project -- these guidelines are attempts to make it *easier* for this to
happen, *not harder*. If you've got any questions, just reach out on
Slack to one of the [subproject leads][kb-leads] (called
@@ -24,32 +24,32 @@ kubebuilder-admins in the `OWNERS_ALIASES` file).
Anyone who wants to become a reviewer or approver must first be a [member
of the Kubernetes project][kube-member]. The aforementioned doc has more
-details, but the gist is that you must have made a couple contributions to
-some part of the Kubernetes project -- *this includes KubeBuilder and
+details, but the gist is that you must have made a couple of contributions to
+some part of the Kubernetes project -- *this includes Kubebuilder and
related repos*. Then, you need two existing members to sponsor you.
-**If you've contributed a few times to KubeBuilder, we'll be happy to
+**If you've contributed a few times to Kubebuilder, we'll be happy to
sponsor you, just ping us on Slack :-)**
## Reviewers
-Reviewers are recongized as able to provide code reviews for parts of the
-codebase, and are entered into the `reviewers` section of one or more
+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,
+codebase and are generally expected to review for correctness,
testing, general code organization, etc. Reviewers may review for design
as well, but approvers have the final say on that.
Things to look for:
-- does this code work, and is it written performantly and idomatically?
+- does this code work, and is it written performantly and idiomatically?
- is it tested?
- is it organized nicely? Is it maintainable?
- is it documented?
- does it need to be threadsafe? Is it?
- Take a glance at the stuff for approvers, if you can.
-Reviewers' `/lgtm` marks are generally trusted by approvers to mean that
+Reviewers' `/lgtm` marks are generally trusted by approvers to means that
the code is ready for one last look-over before merging.
### Becoming a Reviewer
@@ -65,7 +65,7 @@ worked on a cross-cutting feature, it's ok to count PRs across
repositories.
Once you meet those criteria, submit yourself as a reviewer in the
-`OWNERS` file or files that you feel represent your areas of knowlege via
+`OWNERS` file or files that you feel represent your areas of knowledge via
a PR to the relevant repository.
## Approvers
@@ -75,34 +75,34 @@ Once approvals (`/approve`) are given for each piece of the affected code
(and a reviewer or approver has added `/lgtm`), the code will merge.
Approvers are responsible for giving the code a final once-over before
-merge, and doing an overall design/API review.
+merge, and do an overall design/API review.
Things to look for:
- Does the API exposed to the user make sense, and is it easy to use?
-- Is it backwards compatible?
+- Is it backward compatible?
- Will it accommodate new changes in the future?
-- Is it extesnible/layerable (see [DESIGN.md](../DESIGN.md))?
+- Is it extensible/layer-able (see [DESIGN.md](../DESIGN.md))?
- 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.
### Becoming an Approver
-All approvers need to start out as reviewers. The criteria for becoming
-an approver are:
+All approvers need to start as reviewers. The criteria for becoming
+an approver is:
-- Be a reviewer in the area for a couple months
+- Be a reviewer in the area for a couple of months
- Be the "main" reviewer or contributor for 5-10 substantial (bugfixes,
features, etc) PRs where approvers did not need to leave substantial
additional comments (i.e. where you were acting as a defacto approver).
Once you've met those criteria, you can submit yourself as an approver
-using a PR that edits the revelant `OWNERS` files appropriately. The
+using a PR that edits the relevant `OWNERS` files appropriately. The
existing approvers will then approve the change with lazy consensus. If
you feel more comfortable asking before submitting the PR, feel free to
ping one of the [subproject leads][kb-leads] (called kubebuilder-admins in
@@ -110,7 +110,7 @@ the `OWNERS_ALIASES` file) on Slack.
## Indirectly Code-Related/Non-Code Roles
-We're always looking help with other areas of the project as well, such
+We're always looking for help with other areas of the project as well, such
as:
### Docs
@@ -120,15 +120,15 @@ reviewers/approvers for the book by following the same process above.
### Triage
-Help triaging our issues is also welcome. Folks doing triage are
+Help to triage our issues is also welcome. Folks doing triage are
responsible for using the following commands to mark PRs and issues with
one or more labels, and should also feel free to help answer questions:
- `/kind {bug|feature|documentation}`: things that are broken/new
- things/things with lots of words, repsectively
+ things/things with lots of words, respectively
- `/triage support`: questions, and things that might be bugs but might
- just be confusion of how to use something
+ just be confused about how to use something
- `/priority {backlog|important-longterm|important-soon|critical-urgent}`:
how soon we need to deal with the thing (if someone wants
diff --git a/docs/README.md b/docs/README.md
index e6b1dd79ca5..9e90ab3c7ef 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2,11 +2,12 @@
The kubebuilder book is served using [mdBook](https://github.com/rust-lang-nursery/mdBook). If you want to test changes to the book locally, follow these directions:
-1. Follow the instructions at [https://github.com/rust-lang-nursery/mdBook#installation](https://github.com/rust-lang-nursery/mdBook#installation) to
- install mdBook.
-1. cd into the `docs/book` directory
-1. Run `mdbook serve`
-1. Visit [http://localhost:3000](http://localhost:3000)
+1. Follow the instructions at [https://rust-lang.github.io/mdBook/guide/installation.html](https://rust-lang.github.io/mdBook/guide/installation.html) to
+ install mdBook.
+2. Make sure [controller-gen](https://pkg.go.dev/sigs.k8s.io/controller-tools/cmd/controller-gen) is installed in `$GOPATH`.
+3. cd into the `docs/book` directory
+4. Run `mdbook serve`
+5. Visit [http://localhost:3000](http://localhost:3000)
# Steps to deploy
diff --git a/docs/book/book.toml b/docs/book/book.toml
index 7aadb547fc5..569c53f2ab4 100644
--- a/docs/book/book.toml
+++ b/docs/book/book.toml
@@ -5,12 +5,21 @@ src = "src"
title = "The Kubebuilder Book"
[output.html]
-google-analytics = "UA-119864590-1"
-curly-quotes = true
-additional-css = ["theme/css/markers.css", "theme/css/custom.css"]
+default-theme = "light"
+preferred-dark-theme = "navy"
+smart-punctuation = true
+additional-css = ["theme/css/markers.css", "theme/css/custom.css", "theme/css/version-dropdown.css"]
+git-repository-url = "https://github.com/kubernetes-sigs/kubebuilder"
+edit-url-template = "https://github.com/kubernetes-sigs/kubebuilder/edit/master/docs/book/{path}"
[preprocessor.literatego]
command = "./litgo.sh"
[preprocessor.markerdocs]
command = "./markerdocs.sh"
+
+[context.environment]
+ environment = { GO_VERSION = "1.23" }
+
+[context.deploy-preview.environment]
+ environment = { GO_VERSION = "1.23" }
diff --git a/docs/book/functions/handle-version.js b/docs/book/functions/handle-version.js
new file mode 100644
index 00000000000..54b657e8763
--- /dev/null
+++ b/docs/book/functions/handle-version.js
@@ -0,0 +1,37 @@
+function notFound(info) {
+ return {
+ statusCode: 404,
+ headers: {'content-type': 'text/html'},
+ body: ("Not Found "+
+ "You shouldn't see this page, please file a bug
"+
+ `debug details ${JSON.stringify(info)} `
+ ),
+ };
+}
+
+function redirectToDownload(version, file) {
+ const loc = `https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${version}/${file}`;
+ return {
+ statusCode: 302,
+ headers: {'location': loc, 'content-type': 'text/plain'},
+ body: `Redirecting to ${loc}`,
+ };
+}
+
+
+exports.handler = async function(evt, ctx) {
+ // grab the prefix too to check for coherence
+ const [prefix, version, os, arch] = evt.path.split("/").slice(-4);
+ if (prefix !== 'releases' || !version || !os || !arch) {
+ return notFound({version: version, os: os, arch: arch, prefix: prefix, rawPath: evt.path});
+ }
+
+ switch(version[0]) {
+ case '1':
+ // fallthrough
+ case '2':
+ return redirectToDownload(version, `kubebuilder_${version}_${os}_${arch}.tar.gz`);
+ default:
+ return redirectToDownload(version, `kubebuilder_${os}_${arch}`);
+ }
+}
diff --git a/docs/book/install-and-build.sh b/docs/book/install-and-build.sh
index a35e62b0c5d..59c6d8976f2 100755
--- a/docs/book/install-and-build.sh
+++ b/docs/book/install-and-build.sh
@@ -23,7 +23,10 @@ THIS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
cd "$THIS_DIR"
-[[ -n "$(command -v gimme)" ]] && eval "$(gimme stable)"
+if [[ -n "$(command -v gimme)" ]]; then
+ GO_VERSION=${GO_VERSION:-stable} # Use the provided GO_VERSION or default to 'stable'
+ eval "$(gimme $GO_VERSION)"
+fi
echo go version
GOBIN=$THIS_DIR/functions go install ./...
@@ -35,6 +38,10 @@ if [[ ${arch} == "amd64" ]]; then
arch="x86_64"
elif [[ ${arch} == "x86" ]]; then
arch="i686"
+elif [[ ${arch} == "arm64" ]]; then
+ # arm64 is not supported for v0.4.40 mdbook, so using x86_64 type.
+ # Once the mdbook is upgraded to latest, use 'aarch64'
+ arch="x86_64"
fi
# translate os to rust's conventions (if we can)
@@ -60,18 +67,25 @@ esac
# grab mdbook
# we hardcode linux/amd64 since rust uses a different naming scheme and it's a pain to tran
-echo "downloading mdBook-v0.4.2-${arch}-${target}.${ext}"
+MDBOOK_VERSION="v0.4.40"
+MDBOOK_BASENAME="mdBook-${MDBOOK_VERSION}-${arch}-${target}"
+MDBOOK_URL="https://github.com/rust-lang/mdBook/releases/download/${MDBOOK_VERSION}/${MDBOOK_BASENAME}.${ext}"
+
+echo "downloading ${MDBOOK_BASENAME}.${ext} from ${MDBOOK_URL}"
set -x
-curl -sL -o /tmp/mdbook.${ext} https://github.com/rust-lang-nursery/mdBook/releases/download/v0.4.2/mdBook-v0.4.2-${arch}-${target}.${ext}
+curl -fL -o /tmp/mdbook.${ext} "${MDBOOK_URL}"
${cmd} /tmp/mdbook.${ext}
chmod +x /tmp/mdbook
-echo "grabbing the latest released controller-gen"
-go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1
+CONTROLLER_GEN_VERSION="v0.19.0"
+
+echo "grabbing the controller-gen version: ${CONTROLLER_GEN_VERSION}"
+go version
+go install sigs.k8s.io/controller-tools/cmd/controller-gen@${CONTROLLER_GEN_VERSION}
# make sure we add the go bin directory to our path
gobin=$(go env GOBIN)
-gobin=${GOBIN:-$(go env GOPATH)/bin} # GOBIN won't always be set :-/
+gobin=${GOBIN:-$(go env GOPATH)/bin} # GOBIN won't always be set :-/
export PATH=${gobin}:$PATH
verb=${1:-build}
diff --git a/docs/book/src/404.md b/docs/book/src/404.md
deleted file mode 120000
index cde952be386..00000000000
--- a/docs/book/src/404.md
+++ /dev/null
@@ -1 +0,0 @@
-TODO.md
\ No newline at end of file
diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index 1ae4aa010eb..dfb3953d967 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -2,9 +2,11 @@
[Introduction](./introduction.md)
+[Architecture](./architecture.md)
+
[Quick Start](./quick-start.md)
-[Architecture](./architecture.md)
+[Getting Started](./getting-started.md)
---
@@ -16,7 +18,7 @@
- [Adding a new API](./cronjob-tutorial/new-api.md)
- [Designing an API](./cronjob-tutorial/api-design.md)
- - [A Brief Aside: What's the rest of this stuff?](./cronjob-tutorial/other-api-files.md)
+ - [A Brief Aside: What's the rest of this stuff?](./cronjob-tutorial/other-api-files.md)
- [What's in a controller?](./cronjob-tutorial/controller-overview.md)
- [Implementing a controller](./cronjob-tutorial/controller-implementation.md)
@@ -26,85 +28,112 @@
- [Implementing defaulting/validating webhooks](./cronjob-tutorial/webhook-implementation.md)
- [Running and deploying the controller](./cronjob-tutorial/running.md)
- - [Deploying the cert manager](./cronjob-tutorial/cert-manager.md)
+ - [Deploying cert-manager](./cronjob-tutorial/cert-manager.md)
- [Deploying webhooks](./cronjob-tutorial/running-webhook.md)
- [Writing tests](./cronjob-tutorial/writing-tests.md)
- - [Epilogue](./cronjob-tutorial/epilogue.md)
-
- [Tutorial: Multi-Version API](./multiversion-tutorial/tutorial.md)
- [Changing things up](./multiversion-tutorial/api-changes.md)
- [Hubs, spokes, and other wheel metaphors](./multiversion-tutorial/conversion-concepts.md)
- [Implementing conversion](./multiversion-tutorial/conversion.md)
- - [and setting up the webhooks](./multiversion-tutorial/webhooks.md)
+ - [and setting up the webhooks](./multiversion-tutorial/webhooks.md)
- [Deployment and Testing](./multiversion-tutorial/deployment.md)
-- [Tutorial: Component Config](./component-config-tutorial/tutorial.md)
-
- - [Changing things up](./component-config-tutorial/api-changes.md)
- - [Defining your Config](./component-config-tutorial/define-config.md)
-
- - [Using a custom type](./component-config-tutorial/custom-type.md)
-
- - [Adding a new Config Type](./component-config-tutorial/config-type.md)
- - [Updating main](./component-config-tutorial/updating-main.md)
- - [Defining your Custom Config](./component-config-tutorial/define-custom-config.md)
---
- [Migrations](./migrations.md)
- - [Kubebuilder v1 vs v2](./migration/v1vsv2.md)
+ - [Legacy (before <= v3.0.0)](./migration/legacy.md)
+ - [Kubebuilder v1 vs v2](migration/legacy/v1vsv2.md)
- [Migration Guide](./migration/legacy/migration_guide_v1tov2.md)
- - [Kubebuilder v2 vs v3](./migration/v2vsv3.md)
- - [Migration Guide](./migration/migration_guide_v2tov3.md)
- - [Migration by updating the files](./migration/manually_migration_guide_v2_v3.md)
+ - [Kubebuilder v2 vs v3](migration/legacy/v2vsv3.md)
+
+ - [Migration Guide](migration/legacy/migration_guide_v2tov3.md)
+ - [Migration by updating the files](migration/legacy/manually_migration_guide_v2_v3.md)
+ - [From v3.0.0 with plugins](./migration/v3-plugins.md)
+ - [go/v3 vs go/v4](migration/v3vsv4.md)
+ - [Migration Guide](migration/migration_guide_gov3_to_gov4.md)
+ - [Migration by updating the files](migration/manually_migration_guide_gov3_to_gov4.md)
- [Single Group to Multi-Group](./migration/multi-group.md)
+- [Alpha Commands](./reference/alpha_commands.md)
+
+ - [alpha generate](./reference/commands/alpha_generate.md)
+ - [alpha update](./reference/commands/alpha_update.md)
+
---
- [Reference](./reference/reference.md)
- [Generating CRDs](./reference/generating-crd.md)
- [Using Finalizers](./reference/using-finalizers.md)
- - [Kind cluster](reference/kind.md)
+ - [Good Practices](./reference/good-practices.md)
+ - [Raising Events](./reference/raising-events.md)
+ - [Watching Resources](./reference/watching-resources.md)
+ - [Owned Resources](./reference/watching-resources/secondary-owned-resources.md)
+ - [Not Owned Resources](./reference/watching-resources/secondary-resources-not-owned.md)
+ - [Using Predicates](./reference/watching-resources/predicates-with-watch.md)
+ - [Kind for Dev & CI](reference/kind.md)
- [What's a webhook?](reference/webhook-overview.md)
- [Admission webhook](reference/admission-webhook.md)
- - [Webhooks for Core Types](reference/webhook-for-core-types.md)
- [Markers for Config/Code Generation](./reference/markers.md)
- - [CRD Generation](./reference/markers/crd.md)
- - [CRD Validation](./reference/markers/crd-validation.md)
- - [CRD Processing](./reference/markers/crd-processing.md)
- - [Webhook](./reference/markers/webhook.md)
- - [Object/DeepCopy](./reference/markers/object.md)
- - [RBAC](./reference/markers/rbac.md)
+ - [CRD Generation](./reference/markers/crd.md)
+ - [CRD Validation](./reference/markers/crd-validation.md)
+ - [CRD Processing](./reference/markers/crd-processing.md)
+ - [Webhook](./reference/markers/webhook.md)
+ - [Object/DeepCopy](./reference/markers/object.md)
+ - [RBAC](./reference/markers/rbac.md)
+ - [Scaffold](./reference/markers/scaffold.md)
- [controller-gen CLI](./reference/controller-gen.md)
- [completion](./reference/completion.md)
- [Artifacts](./reference/artifacts.md)
+ - [Platform Support](./reference/platform.md)
+ - [Monitoring with Pprof](./reference/pprof-tutorial.md)
+
+ - [Manager and CRDs Scope](./reference/scopes.md)
+
+ - [Sub-Module Layouts](./reference/submodule-layouts.md)
+ - [Using an external Resource / API](./reference/using_an_external_resource.md)
- [Configuring EnvTest](./reference/envtest.md)
- [Metrics](./reference/metrics.md)
- - [Makefile Helpers](./reference/makefile-helpers.md)
+
+ - [Reference](./reference/metrics-reference.md)
+
- [Project config](./reference/project-config.md)
+ - [Versions Compatibility and Supportability](./versions_compatibility_supportability.md)
---
- [Plugins][plugins]
- - [Extending the CLI](./plugins/extending-cli.md)
- - [Creating your own plugins](./plugins/creating-plugins.md)
+ - [Available Plugins](./plugins/available-plugins.md)
+ - [autoupdate/v1-alpha](./plugins/available/autoupdate-v1-alpha.md)
+ - [deploy-image/v1-alpha](./plugins/available/deploy-image-plugin-v1-alpha.md)
+ - [go/v4](./plugins/available/go-v4-plugin.md)
+ - [grafana/v1-alpha](./plugins/available/grafana-v1-alpha.md)
+ - [helm/v1-alpha](./plugins/available/helm-v1-alpha.md)
+ - [helm/v2-alpha](./plugins/available/helm-v2-alpha.md)
+ - [kustomize/v2](./plugins/available/kustomize-v2.md)
+ - [Extending](./plugins/extending.md)
+ - [CLI and Plugins](./plugins/extending/extending_cli_features_and_plugins.md)
+ - [External Plugins](./plugins/extending/external-plugins.md)
+ - [E2E Tests](./plugins/extending/testing-plugins.md)
+ - [Plugins Versioning](./plugins/plugins-versioning.md)
----
-[Appendix: The TODO Landing Page](./TODO.md)
+---
+
+[FAQ](./faq.md)
-[plugins]: ./plugins/plugins.md
\ No newline at end of file
+[plugins]: ./plugins/plugins.md
diff --git a/docs/book/src/TODO.md b/docs/book/src/TODO.md
index 37a00404fe5..f69f6cae0e9 100644
--- a/docs/book/src/TODO.md
+++ b/docs/book/src/TODO.md
@@ -1,8 +1,17 @@
-# TODO
-
-If you're seeing this page, it's probably because something's not done in
-the book yet, or you stumbled upon an old link. Go [see if anyone else
-has found
-this](https://github.com/kubernetes-sigs/kubebuilder/issues?q=is%3Aopen+is%3Aissue+label%3Akind%2Fdocumentation)
-or [bug the
-maintainers](https://github.com/kubernetes-sigs/kubebuilder/issues/new?assignees=&labels=kind%2Fdocumentation).
+# Page Not Found
+
+The page you are looking for could not be found. This might be because:
+
+1. The page has been moved or renamed
+2. The page is no longer available
+3. The URL was entered incorrectly
+
+Please try:
+
+- Going back to the [home page](https://book.kubebuilder.io/)
+- Using the search function
+- Suggest an edit [documentation index](https://github.com/kubernetes-sigs/kubebuilder/tree/master/docs/book/src)
+
+Check out if someone is working on your issue [report an issue](https://github.com/kubernetes-sigs/kubebuilder/issues)
+If you believe this is an error, please [report an issue](https://github.com/kubernetes-sigs/kubebuilder/issues/new?template=BLANK_ISSUE)
+Reach out to us on [Slack](https://kubernetes.slack.com/messages/kubebuilder)
\ No newline at end of file
diff --git a/docs/book/src/architecture.md b/docs/book/src/architecture.md
index ef12bd22fa2..bccb71898c1 100644
--- a/docs/book/src/architecture.md
+++ b/docs/book/src/architecture.md
@@ -1,6 +1,6 @@
# Architecture Concept Diagram
-The following diagram will help you get a better idea over the Kubebuilder concepts and architecture.
+The following diagram will help you get a better idea over the Kubebuilder concepts and architecture.
{{#include ./kb_concept_diagram.svg}}
diff --git a/docs/book/src/component-config-tutorial/api-changes.md b/docs/book/src/component-config-tutorial/api-changes.md
deleted file mode 100644
index 5094aa02430..00000000000
--- a/docs/book/src/component-config-tutorial/api-changes.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Changing things up
-
-This tutorial will show you how to create a custom configuration file for your
-project by modifying a project generated with the `--component-config` flag
-passed to the `init` command. The full tutorial's source can be found
-[here][tutorial-source]. Make sure you've gone through the [installation
-steps](/quick-start.md#installation) before continuing.
-
-## New project:
-
-```bash
-# we'll use a domain of tutorial.kubebuilder.io,
-# so all API groups will be .tutorial.kubebuilder.io.
-kubebuilder init --domain tutorial.kubebuilder.io --component-config
-```
-
-## Setting up an exising project
-
-If you've previously generated a project we can add support for parsing the
-config file by making the following changes to `main.go`.
-
-First, add a new `flag` to specify the path that the component config file
-should be loaded from.
-
-```go
-var configFile string
-flag.StringVar(&configFile, "config", "",
- "The controller will load its initial configuration from this file. "+
- "Omit this flag to use the default configuration values. "+
- "Command-line flags override configuration from this file."
-```
-
-Now, we can setup the `Options` struct and check if the `configFile` is set,
-this allows backwards compatibility, if it's set we'll then use the `AndFrom`
-function on `Options` to parse and populate the `Options` from the config.
-
-
-```go
-var err error
-options := ctrl.Options{Scheme: scheme}
-if configFile != "" {
- options, err = options.AndFrom(ctrl.ConfigFile().AtPath(configFile))
- if err != nil {
- setupLog.Error(err, "unable to load the config file")
- os.Exit(1)
- }
-}
-```
-
-
-
-Your Options may have defaults from flags.
-
-If you have previously allowed other `flags` like `--metrics-bind-addr` or
-`--enable-leader-election`, you'll want to set those on the `Options` before
-loading the config from the file.
-
-
-
-Lastly, we'll change the `NewManager` call to use the `options` varible we
-defined above.
-
-```go
-mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
-```
-
-With that out of the way, we can get on to defining our new config!
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/config-type.md b/docs/book/src/component-config-tutorial/config-type.md
deleted file mode 100644
index 81630bb9180..00000000000
--- a/docs/book/src/component-config-tutorial/config-type.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Adding a new Config Type
-
-To scaffold out a new config Kind, we can use `kubebuilder create api`.
-
-```bash
-kubebuilder create api --group config --version v2 --kind ProjectConfig --resource --controller=false --make=false
-```
-
-
-
-
-Use --controller=false
-
-You may notice this command from the `CronJob` tutorial although here we
-explicity setting `--controller=false` because `ProjectConfig` is not
-intended to be an API extension and cannot be reconciled.
-
-
-
-This will create a new type file in `apis/config/v2/` for the `ProjectConfig`
-kind. We'll need to change this file to embed the
-[v1alpha1.ControllerManagerConfigurationSpec](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/#ControllerManagerConfigurationSpec)
-
-{{#literatego ./testdata/projectconfig_types.go}}
-
-Lastly, we'll change the `main.go` to reference this type for parsing the file.
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/custom-type.md b/docs/book/src/component-config-tutorial/custom-type.md
deleted file mode 100644
index 76dfeda43d2..00000000000
--- a/docs/book/src/component-config-tutorial/custom-type.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Using a Custom Type
-
-
-
-Built-in vs Custom Type
-
-If you don't need to add custom fields to configure your project you can stop
-now and move on, if you'd like to be able to pass additional information keep
-reading.
-
-
-
-If your project needs to accept additional non-controller runtime specific
-configurations, e.g. `ClusterName`, `Region` or anything serializable into
-`yaml` you can do this by using `kubebuilder` to create a new type and then
-updating your `main.go` to setup the new type for parsing.
-
-The rest of this tutorial will walk through implementing a custom component
-config type.
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/define-config.md b/docs/book/src/component-config-tutorial/define-config.md
deleted file mode 100644
index f22e753bca8..00000000000
--- a/docs/book/src/component-config-tutorial/define-config.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Defining your Config
-
-Now that you have a component config base project we need to customize the
-values that are passed into the controller, to do this we can take a look at
-`config/manager/controller_manager_config.yaml`.
-
-{{#literatego ./testdata/controller_manager_config.yaml}}
-
-To see all the available fields you can look at the `v1alpha` Controller
-Runtime config [ControllerManagerConfiguration](configtype)
-
-[configtype]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/#ControllerManagerConfigurationSpec
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/define-custom-config.md b/docs/book/src/component-config-tutorial/define-custom-config.md
deleted file mode 100644
index fcd3e3f0490..00000000000
--- a/docs/book/src/component-config-tutorial/define-custom-config.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Defining your Custom Config
-
-Now that you have a custom component config we change the
-`config/manager/controller_manager_config.yaml` to use the new GVK you defined.
-
-{{#literatego ./testdata/project/config/manager/controller_manager_config.yaml}}
-
-This type uses the new `ProjectConfig` kind under the GVK
-`config.tutorial.kubebuilder.io/v2`, with these custom configs we can add any
-`yaml` serializable fields that your controller needs and begin to reduce the
-reliance on `flags` to configure your project.
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/testdata/controller_manager_config.yaml b/docs/book/src/component-config-tutorial/testdata/controller_manager_config.yaml
deleted file mode 100644
index cb5e0786bd3..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/controller_manager_config.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
-kind: ControllerManagerConfig
-metrics:
- bindAddress: 127.0.0.1:8080
-webhook:
- port: 9443
-leaderElection:
- leaderElect: true
- resourceName: 80807133.tutorial.kubebuilder.io
diff --git a/docs/book/src/component-config-tutorial/testdata/project/.dockerignore b/docs/book/src/component-config-tutorial/testdata/project/.dockerignore
deleted file mode 100644
index 243f81a5080..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/.dockerignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
-# Ignore all files which are not go type
-!**/*.go
-!**/*.mod
-!**/*.sum
diff --git a/docs/book/src/component-config-tutorial/testdata/project/.gitignore b/docs/book/src/component-config-tutorial/testdata/project/.gitignore
deleted file mode 100644
index c0a7a54cac5..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/.gitignore
+++ /dev/null
@@ -1,25 +0,0 @@
-
-# Binaries for programs and plugins
-*.exe
-*.exe~
-*.dll
-*.so
-*.dylib
-bin
-testbin/*
-
-# Test binary, build with `go test -c`
-*.test
-
-# Output of the go coverage tool, specifically when used with LiteIDE
-*.out
-
-# Kubernetes Generated files - skip generated files, except for vendored files
-
-!vendor/**/zz_generated.*
-
-# editor and IDE paraphernalia
-.idea
-*.swp
-*.swo
-*~
diff --git a/docs/book/src/component-config-tutorial/testdata/project/Dockerfile b/docs/book/src/component-config-tutorial/testdata/project/Dockerfile
deleted file mode 100644
index 5169a8c09c9..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/Dockerfile
+++ /dev/null
@@ -1,27 +0,0 @@
-# Build the manager binary
-FROM golang:1.15 as builder
-
-WORKDIR /workspace
-# Copy the Go Modules manifests
-COPY go.mod go.mod
-COPY go.sum go.sum
-# cache deps before building and copying source so that we don't need to re-download as much
-# and so that source changes don't invalidate our downloaded layer
-RUN go mod download
-
-# Copy the go source
-COPY main.go main.go
-COPY apis/ apis/
-COPY controllers/ controllers/
-
-# Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
-
-# Use distroless as minimal base image to package the manager binary
-# Refer to https://github.com/GoogleContainerTools/distroless for more details
-FROM gcr.io/distroless/static:nonroot
-WORKDIR /
-COPY --from=builder /workspace/manager .
-USER 65532:65532
-
-ENTRYPOINT ["/manager"]
diff --git a/docs/book/src/component-config-tutorial/testdata/project/Makefile b/docs/book/src/component-config-tutorial/testdata/project/Makefile
deleted file mode 100644
index ca60fbd4890..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/Makefile
+++ /dev/null
@@ -1,94 +0,0 @@
-
-# Image URL to use all building/pushing image targets
-IMG ?= controller:latest
-# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
-CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
-
-# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
-ifeq (,$(shell go env GOBIN))
-GOBIN=$(shell go env GOPATH)/bin
-else
-GOBIN=$(shell go env GOBIN)
-endif
-
-all: manager
-
-# Run tests
-ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
-test: generate fmt vet manifests
- mkdir -p ${ENVTEST_ASSETS_DIR}
- test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.0-alpha.6/hack/setup-envtest.sh
- source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
-
-# Build manager binary
-manager: generate fmt vet
- go build -o bin/manager main.go
-
-# Run against the configured Kubernetes cluster in ~/.kube/config
-run: generate fmt vet manifests
- go run ./main.go
-
-# Install CRDs into a cluster
-install: manifests kustomize
- $(KUSTOMIZE) build config/crd | kubectl apply -f -
-
-# Uninstall CRDs from a cluster
-uninstall: manifests kustomize
- $(KUSTOMIZE) build config/crd | kubectl delete -f -
-
-# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
-deploy: manifests kustomize
- cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
- $(KUSTOMIZE) build config/default | kubectl apply -f -
-
-# UnDeploy controller from the configured Kubernetes cluster in ~/.kube/config
-undeploy:
- $(KUSTOMIZE) build config/default | kubectl delete -f -
-
-# Generate manifests e.g. CRD, RBAC etc.
-manifests: controller-gen
- $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
-
-# Run go fmt against code
-fmt:
- go fmt ./...
-
-# Run go vet against code
-vet:
- go vet ./...
-
-# Generate code
-generate: controller-gen
- $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
-
-# Build the docker image
-docker-build: test
- docker build -t ${IMG} .
-
-# Push the docker image
-docker-push:
- docker push ${IMG}
-
-# Download controller-gen locally if necessary
-CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
-controller-gen:
- $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
-
-# Download kustomize locally if necessary
-KUSTOMIZE = $(shell pwd)/bin/kustomize
-kustomize:
- $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
-
-# go-get-tool will 'go get' any package $2 and install it to $1.
-PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
-define go-get-tool
-@[ -f $(1) ] || { \
-set -e ;\
-TMP_DIR=$$(mktemp -d) ;\
-cd $$TMP_DIR ;\
-go mod init tmp ;\
-echo "Downloading $(2)" ;\
-GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
-rm -rf $$TMP_DIR ;\
-}
-endef
diff --git a/docs/book/src/component-config-tutorial/testdata/project/PROJECT b/docs/book/src/component-config-tutorial/testdata/project/PROJECT
deleted file mode 100644
index ffc6d718274..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/PROJECT
+++ /dev/null
@@ -1,27 +0,0 @@
-componentConfig: true
-domain: tutorial.kubebuilder.io
-layout: go.kubebuilder.io/v3
-multigroup: true
-projectName: project
-repo: tutorial.kubebuilder.io/project
-resources:
-- api:
- crdVersion: v1
- group: batch
- kind: CronJob
- version: v1
- webhooks:
- webhookVersion: v1
-- api:
- crdVersion: v1
- group: batch
- kind: CronJob
- version: v2
- webhooks:
- webhookVersion: v1
-- api:
- crdVersion: v1
- group: config
- kind: ProjectConfig
- version: v2
-version: "3"
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/cronjob_types.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/cronjob_types.go
deleted file mode 100644
index 762cc534136..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/cronjob_types.go
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v1
-
-import (
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
-// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
-
-// CronJobSpec defines the desired state of CronJob
-type CronJobSpec struct {
- // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
- // Important: Run "make" to regenerate code after modifying this file
-
- // Foo is an example field of CronJob. Edit CronJob_types.go to remove/update
- Foo string `json:"foo,omitempty"`
-}
-
-// CronJobStatus defines the observed state of CronJob
-type CronJobStatus struct {
- // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
- // Important: Run "make" to regenerate code after modifying this file
-}
-
-// +kubebuilder:object:root=true
-// +kubebuilder:subresource:status
-
-// CronJob is the Schema for the cronjobs API
-type CronJob struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
-
- Spec CronJobSpec `json:"spec,omitempty"`
- Status CronJobStatus `json:"status,omitempty"`
-}
-
-// +kubebuilder:object:root=true
-
-// CronJobList contains a list of CronJob
-type CronJobList struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata,omitempty"`
- Items []CronJob `json:"items"`
-}
-
-func init() {
- SchemeBuilder.Register(&CronJob{}, &CronJobList{})
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/cronjob_webhook.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/cronjob_webhook.go
deleted file mode 100644
index 14c58b57ce3..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/cronjob_webhook.go
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v1
-
-import (
- "k8s.io/apimachinery/pkg/runtime"
- ctrl "sigs.k8s.io/controller-runtime"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
-)
-
-// log is for logging in this package.
-var cronjoblog = logf.Log.WithName("cronjob-resource")
-
-func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(r).
- Complete()
-}
-
-// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
-
-// +kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
-
-var _ webhook.Defaulter = &CronJob{}
-
-// Default implements webhook.Defaulter so a webhook will be registered for the type
-func (r *CronJob) Default() {
- cronjoblog.Info("default", "name", r.Name)
-
- // TODO(user): fill in your defaulting logic.
-}
-
-// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
-// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=vcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
-
-var _ webhook.Validator = &CronJob{}
-
-// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateCreate() error {
- cronjoblog.Info("validate create", "name", r.Name)
-
- // TODO(user): fill in your validation logic upon object creation.
- return nil
-}
-
-// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateUpdate(old runtime.Object) error {
- cronjoblog.Info("validate update", "name", r.Name)
-
- // TODO(user): fill in your validation logic upon object update.
- return nil
-}
-
-// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateDelete() error {
- cronjoblog.Info("validate delete", "name", r.Name)
-
- // TODO(user): fill in your validation logic upon object deletion.
- return nil
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/groupversion_info.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/groupversion_info.go
deleted file mode 100644
index a347e9880b7..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/groupversion_info.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package v1 contains API Schema definitions for the batch v1 API group
-// +kubebuilder:object:generate=true
-// +groupName=batch.tutorial.kubebuilder.io
-package v1
-
-import (
- "k8s.io/apimachinery/pkg/runtime/schema"
- "sigs.k8s.io/controller-runtime/pkg/scheme"
-)
-
-var (
- // GroupVersion is group version used to register these objects
- GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v1"}
-
- // SchemeBuilder is used to add go types to the GroupVersionKind scheme
- SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
-
- // AddToScheme adds the types in this group-version to the given scheme.
- AddToScheme = SchemeBuilder.AddToScheme
-)
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/webhook_suite_test.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/webhook_suite_test.go
deleted file mode 100644
index 7dc14b3a67d..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/webhook_suite_test.go
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v1
-
-import (
- "context"
- "crypto/tls"
- "fmt"
- "net"
- "path/filepath"
- "testing"
- "time"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
-
- admissionv1beta1 "k8s.io/api/admission/v1beta1"
- // +kubebuilder:scaffold:imports
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/rest"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-)
-
-// 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
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Webhook Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- ctx, cancel = context.WithCancel(context.TODO())
-
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
- WebhookInstallOptions: envtest.WebhookInstallOptions{
- Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
- },
- }
-
- cfg, err := testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
-
- scheme := runtime.NewScheme()
- err = AddToScheme(scheme)
- Expect(err).NotTo(HaveOccurred())
-
- err = admissionv1beta1.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())
-
- // start webhook server using Manager
- webhookInstallOptions := &testEnv.WebhookInstallOptions
- mgr, err := ctrl.NewManager(cfg, ctrl.Options{
- Scheme: scheme,
- Host: webhookInstallOptions.LocalServingHost,
- Port: webhookInstallOptions.LocalServingPort,
- CertDir: webhookInstallOptions.LocalServingCertDir,
- LeaderElection: false,
- MetricsBindAddress: "0",
- })
- Expect(err).NotTo(HaveOccurred())
-
- err = (&CronJob{}).SetupWebhookWithManager(mgr)
- Expect(err).NotTo(HaveOccurred())
-
- // +kubebuilder:scaffold:webhook
-
- go func() {
- err = mgr.Start(ctx)
- if err != nil {
- Expect(err).NotTo(HaveOccurred())
- }
- }()
-
- // wait for the webhook server to get ready
- dialer := &net.Dialer{Timeout: time.Second}
- addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
- Eventually(func() error {
- conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
- if err != nil {
- return err
- }
- conn.Close()
- return nil
- }).Should(Succeed())
-
-}, 60)
-
-var _ = AfterSuite(func() {
- cancel()
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/zz_generated.deepcopy.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/zz_generated.deepcopy.go
deleted file mode 100644
index ee660b43350..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v1/zz_generated.deepcopy.go
+++ /dev/null
@@ -1,114 +0,0 @@
-// +build !ignore_autogenerated
-
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by controller-gen. DO NOT EDIT.
-
-package v1
-
-import (
- "k8s.io/apimachinery/pkg/runtime"
-)
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJob) DeepCopyInto(out *CronJob) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
- out.Spec = in.Spec
- out.Status = in.Status
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJob.
-func (in *CronJob) DeepCopy() *CronJob {
- if in == nil {
- return nil
- }
- out := new(CronJob)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *CronJob) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJobList) DeepCopyInto(out *CronJobList) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ListMeta.DeepCopyInto(&out.ListMeta)
- if in.Items != nil {
- in, out := &in.Items, &out.Items
- *out = make([]CronJob, len(*in))
- for i := range *in {
- (*in)[i].DeepCopyInto(&(*out)[i])
- }
- }
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobList.
-func (in *CronJobList) DeepCopy() *CronJobList {
- if in == nil {
- return nil
- }
- out := new(CronJobList)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *CronJobList) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) {
- *out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobSpec.
-func (in *CronJobSpec) DeepCopy() *CronJobSpec {
- if in == nil {
- return nil
- }
- out := new(CronJobSpec)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJobStatus) DeepCopyInto(out *CronJobStatus) {
- *out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobStatus.
-func (in *CronJobStatus) DeepCopy() *CronJobStatus {
- if in == nil {
- return nil
- }
- out := new(CronJobStatus)
- in.DeepCopyInto(out)
- return out
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/cronjob_types.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/cronjob_types.go
deleted file mode 100644
index 955b684e7fd..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/cronjob_types.go
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
-// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
-
-// CronJobSpec defines the desired state of CronJob
-type CronJobSpec struct {
- // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
- // Important: Run "make" to regenerate code after modifying this file
-
- // Foo is an example field of CronJob. Edit CronJob_types.go to remove/update
- Foo string `json:"foo,omitempty"`
-}
-
-// CronJobStatus defines the observed state of CronJob
-type CronJobStatus struct {
- // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
- // Important: Run "make" to regenerate code after modifying this file
-}
-
-// +kubebuilder:object:root=true
-// +kubebuilder:subresource:status
-
-// CronJob is the Schema for the cronjobs API
-type CronJob struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
-
- Spec CronJobSpec `json:"spec,omitempty"`
- Status CronJobStatus `json:"status,omitempty"`
-}
-
-// +kubebuilder:object:root=true
-
-// CronJobList contains a list of CronJob
-type CronJobList struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata,omitempty"`
- Items []CronJob `json:"items"`
-}
-
-func init() {
- SchemeBuilder.Register(&CronJob{}, &CronJobList{})
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/cronjob_webhook.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/cronjob_webhook.go
deleted file mode 100644
index 4063eaf780b..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/cronjob_webhook.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
- ctrl "sigs.k8s.io/controller-runtime"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
-)
-
-// log is for logging in this package.
-var cronjoblog = logf.Log.WithName("cronjob-resource")
-
-func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(r).
- Complete()
-}
-
-// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/groupversion_info.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/groupversion_info.go
deleted file mode 100644
index cf044afb360..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/groupversion_info.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package v2 contains API Schema definitions for the batch v2 API group
-// +kubebuilder:object:generate=true
-// +groupName=batch.tutorial.kubebuilder.io
-package v2
-
-import (
- "k8s.io/apimachinery/pkg/runtime/schema"
- "sigs.k8s.io/controller-runtime/pkg/scheme"
-)
-
-var (
- // GroupVersion is group version used to register these objects
- GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v2"}
-
- // SchemeBuilder is used to add go types to the GroupVersionKind scheme
- SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
-
- // AddToScheme adds the types in this group-version to the given scheme.
- AddToScheme = SchemeBuilder.AddToScheme
-)
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/zz_generated.deepcopy.go b/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/zz_generated.deepcopy.go
deleted file mode 100644
index ef3de33a447..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/batch/v2/zz_generated.deepcopy.go
+++ /dev/null
@@ -1,114 +0,0 @@
-// +build !ignore_autogenerated
-
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Code generated by controller-gen. DO NOT EDIT.
-
-package v2
-
-import (
- runtime "k8s.io/apimachinery/pkg/runtime"
-)
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJob) DeepCopyInto(out *CronJob) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
- out.Spec = in.Spec
- out.Status = in.Status
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJob.
-func (in *CronJob) DeepCopy() *CronJob {
- if in == nil {
- return nil
- }
- out := new(CronJob)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *CronJob) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJobList) DeepCopyInto(out *CronJobList) {
- *out = *in
- out.TypeMeta = in.TypeMeta
- in.ListMeta.DeepCopyInto(&out.ListMeta)
- if in.Items != nil {
- in, out := &in.Items, &out.Items
- *out = make([]CronJob, len(*in))
- for i := range *in {
- (*in)[i].DeepCopyInto(&(*out)[i])
- }
- }
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobList.
-func (in *CronJobList) DeepCopy() *CronJobList {
- if in == nil {
- return nil
- }
- out := new(CronJobList)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
-func (in *CronJobList) DeepCopyObject() runtime.Object {
- if c := in.DeepCopy(); c != nil {
- return c
- }
- return nil
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) {
- *out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobSpec.
-func (in *CronJobSpec) DeepCopy() *CronJobSpec {
- if in == nil {
- return nil
- }
- out := new(CronJobSpec)
- in.DeepCopyInto(out)
- return out
-}
-
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *CronJobStatus) DeepCopyInto(out *CronJobStatus) {
- *out = *in
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobStatus.
-func (in *CronJobStatus) DeepCopy() *CronJobStatus {
- if in == nil {
- return nil
- }
- out := new(CronJobStatus)
- in.DeepCopyInto(out)
- return out
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/config/v2/groupversion_info.go b/docs/book/src/component-config-tutorial/testdata/project/apis/config/v2/groupversion_info.go
deleted file mode 100644
index fe97d302478..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/config/v2/groupversion_info.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package v2 contains API Schema definitions for the config v2 API group
-// +kubebuilder:object:generate=true
-// +groupName=config.tutorial.kubebuilder.io
-package v2
-
-import (
- "k8s.io/apimachinery/pkg/runtime/schema"
- "sigs.k8s.io/controller-runtime/pkg/scheme"
-)
-
-var (
- // GroupVersion is group version used to register these objects
- GroupVersion = schema.GroupVersion{Group: "config.tutorial.kubebuilder.io", Version: "v2"}
-
- // SchemeBuilder is used to add go types to the GroupVersionKind scheme
- SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
-
- // AddToScheme adds the types in this group-version to the given scheme.
- AddToScheme = SchemeBuilder.AddToScheme
-)
diff --git a/docs/book/src/component-config-tutorial/testdata/project/apis/config/v2/projectconfig_types.go b/docs/book/src/component-config-tutorial/testdata/project/apis/config/v2/projectconfig_types.go
deleted file mode 100644
index d8860aa335f..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/apis/config/v2/projectconfig_types.go
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- cfg "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
-)
-
-// +kubebuilder:object:root=true
-
-// ProjectConfig is the Schema for the projectconfigs API
-type ProjectConfig struct {
- metav1.TypeMeta `json:",inline"`
-
- // ControllerManagerConfigurationSpec returns the contfigurations for controllers
- cfg.ControllerManagerConfigurationSpec `json:",inline"`
-
- ClusterName string `json:"clusterName,omitempty"`
-}
-
-func init() {
- SchemeBuilder.Register(&ProjectConfig{})
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/certificate.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/certificate.yaml
deleted file mode 100644
index 52d866183c7..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/certificate.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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:
- name: selfsigned-issuer
- namespace: system
-spec:
- selfSigned: {}
----
-apiVersion: cert-manager.io/v1
-kind: Certificate
-metadata:
- 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
- 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
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/kustomization.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/kustomization.yaml
deleted file mode 100644
index bebea5a595e..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/kustomization.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-resources:
-- certificate.yaml
-
-configurations:
-- kustomizeconfig.yaml
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
deleted file mode 100644
index 90d7c313ca1..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-# This configuration is for teaching kustomize how to update name ref and var substitution
-nameReference:
-- kind: Issuer
- group: cert-manager.io
- fieldSpecs:
- - kind: Certificate
- group: cert-manager.io
- path: spec/issuerRef/name
-
-varReference:
-- kind: Certificate
- group: cert-manager.io
- path: spec/commonName
-- kind: Certificate
- group: cert-manager.io
- path: spec/dnsNames
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/crd/kustomization.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/crd/kustomization.yaml
deleted file mode 100644
index fb5365a0de0..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/crd/kustomization.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-# This kustomization.yaml is not intended to be run by itself,
-# since it depends on service name and namespace that are out of this kustomize package.
-# It should be run by config/default
-resources:
-- bases/batch.tutorial.kubebuilder.io_cronjobs.yaml
-- bases/config.tutorial.kubebuilder.io_projectconfigs.yaml
-# +kubebuilder:scaffold:crdkustomizeresource
-
-patchesStrategicMerge:
-# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
-# patches here are for enabling the conversion webhook for each CRD
-#- patches/webhook_in_cronjobs.yaml
-#- patches/webhook_in_projectconfigs.yaml
-# +kubebuilder:scaffold:crdkustomizewebhookpatch
-
-# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
-# patches here are for enabling the CA injection for each CRD
-#- patches/cainjection_in_cronjobs.yaml
-#- patches/cainjection_in_projectconfigs.yaml
-# +kubebuilder:scaffold:crdkustomizecainjectionpatch
-
-# the following config is for teaching kustomize how to do kustomization for CRDs.
-configurations:
-- kustomizeconfig.yaml
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml
deleted file mode 100644
index 7b037c0e41f..00000000000
--- a/docs/book/src/component-config-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/component-config-tutorial/testdata/project/config/crd/patches/cainjection_in_projectconfigs.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/cainjection_in_projectconfigs.yaml
deleted file mode 100644
index d655ae5f8c7..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/cainjection_in_projectconfigs.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: projectconfigs.config.tutorial.kubebuilder.io
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
deleted file mode 100644
index 76d1d9a3744..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-# The following patch enables a conversion webhook for the CRD
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
- name: cronjobs.batch.tutorial.kubebuilder.io
-spec:
- conversion:
- strategy: Webhook
- webhook:
- clientConfig:
- service:
- namespace: system
- name: webhook-service
- path: /convert
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/webhook_in_projectconfigs.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/webhook_in_projectconfigs.yaml
deleted file mode 100644
index c172b76a589..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/crd/patches/webhook_in_projectconfigs.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-# The following patch enables a conversion webhook for the CRD
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
- name: projectconfigs.config.tutorial.kubebuilder.io
-spec:
- conversion:
- strategy: Webhook
- webhook:
- clientConfig:
- service:
- namespace: system
- name: webhook-service
- path: /convert
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/default/kustomization.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/default/kustomization.yaml
deleted file mode 100644
index 272d2132c6d..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/default/kustomization.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-# Adds namespace to all resources.
-namespace: project-system
-
-# Value of this field is prepended to the
-# names of all resources, e.g. a deployment named
-# "wordpress" becomes "alices-wordpress".
-# Note that it should also match with the prefix (text before '-') of the namespace
-# field above.
-namePrefix: project-
-
-# Labels to add to all resources and selectors.
-#commonLabels:
-# someName: someValue
-
-bases:
-- ../crd
-- ../rbac
-- ../manager
-# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
-# crd/kustomization.yaml
-#- ../webhook
-# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
-#- ../certmanager
-# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
-#- ../prometheus
-
-patchesStrategicMerge:
-# Protect the /metrics endpoint by putting it behind auth.
-# If you want your controller-manager to expose the /metrics
-# endpoint w/o any authn/z, please comment the following line.
-- manager_auth_proxy_patch.yaml
-
-# Mount the controller config file for loading manager configurations
-# through a ComponentConfig type
-- manager_config_patch.yaml
-
-# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
-# crd/kustomization.yaml
-#- manager_webhook_patch.yaml
-
-# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
-# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
-# 'CERTMANAGER' needs to be enabled to use ca injection
-#- webhookcainjection_patch.yaml
-
-# the following config is for teaching kustomize how to do var substitution
-vars:
-# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
-#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
-# objref:
-# kind: Certificate
-# group: cert-manager.io
-# version: v1
-# name: serving-cert # this name should match the one in certificate.yaml
-# fieldref:
-# fieldpath: metadata.namespace
-#- name: CERTIFICATE_NAME
-# objref:
-# kind: Certificate
-# group: cert-manager.io
-# version: v1
-# name: serving-cert # this name should match the one in certificate.yaml
-#- name: SERVICE_NAMESPACE # namespace of the service
-# objref:
-# kind: Service
-# version: v1
-# name: webhook-service
-# fieldref:
-# fieldpath: metadata.namespace
-#- name: SERVICE_NAME
-# objref:
-# kind: Service
-# version: v1
-# name: webhook-service
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml
deleted file mode 100644
index cf923e8c88a..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-# This patch inject a sidecar container which is a HTTP proxy for the
-# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-spec:
- template:
- spec:
- containers:
- - name: kube-rbac-proxy
- image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
- args:
- - "--secure-listen-address=0.0.0.0:8443"
- - "--upstream=http://127.0.0.1:8080/"
- - "--logtostderr=true"
- - "--v=10"
- ports:
- - containerPort: 8443
- name: https
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_config_patch.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_config_patch.yaml
deleted file mode 100644
index 6c400155cfb..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_config_patch.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-spec:
- template:
- spec:
- containers:
- - name: manager
- args:
- - "--config=controller_manager_config.yaml"
- volumeMounts:
- - name: manager-config
- mountPath: /controller_manager_config.yaml
- subPath: controller_manager_config.yaml
- volumes:
- - name: manager-config
- configMap:
- name: manager-config
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_webhook_patch.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_webhook_patch.yaml
deleted file mode 100644
index 738de350b71..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/default/manager_webhook_patch.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-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
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml
deleted file mode 100644
index 02ab515d428..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# This patch add annotation to admission webhook config and
-# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
-apiVersion: admissionregistration.k8s.io/v1
-kind: MutatingWebhookConfiguration
-metadata:
- name: mutating-webhook-configuration
- annotations:
- cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
----
-apiVersion: admissionregistration.k8s.io/v1
-kind: ValidatingWebhookConfiguration
-metadata:
- name: validating-webhook-configuration
- annotations:
- cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/manager/controller_manager_config.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/manager/controller_manager_config.yaml
deleted file mode 100644
index 4883af694c9..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/manager/controller_manager_config.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: config.tutorial.kubebuilder.io/v2
-kind: ProjectConfig
-metrics:
- bindAddress: 127.0.0.1:8080
-webhook:
- port: 9443
-leaderElection:
- leaderElect: true
- resourceName: 80807133.tutorial.kubebuilder.io
-clusterName: example-test
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/manager/kustomization.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/manager/kustomization.yaml
deleted file mode 100644
index 2bcd3eeaa94..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/manager/kustomization.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-resources:
-- manager.yaml
-
-generatorOptions:
- disableNameSuffixHash: true
-
-configMapGenerator:
-- name: manager-config
- files:
- - controller_manager_config.yaml
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/manager/manager.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/manager/manager.yaml
deleted file mode 100644
index 9f691b3f55b..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/manager/manager.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-apiVersion: v1
-kind: Namespace
-metadata:
- labels:
- control-plane: controller-manager
- name: system
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
- labels:
- control-plane: controller-manager
-spec:
- selector:
- matchLabels:
- control-plane: controller-manager
- replicas: 1
- template:
- metadata:
- labels:
- control-plane: controller-manager
- spec:
- securityContext:
- runAsUser: 65532
- containers:
- - command:
- - /manager
- image: controller:latest
- name: manager
- securityContext:
- allowPrivilegeEscalation: false
- resources:
- limits:
- cpu: 100m
- memory: 30Mi
- requests:
- cpu: 100m
- memory: 20Mi
- terminationGracePeriodSeconds: 10
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/prometheus/kustomization.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/prometheus/kustomization.yaml
deleted file mode 100644
index ed137168a1d..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/prometheus/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
-- monitor.yaml
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/prometheus/monitor.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/prometheus/monitor.yaml
deleted file mode 100644
index d19136ae710..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/prometheus/monitor.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-# Prometheus Monitor Service (Metrics)
-apiVersion: monitoring.coreos.com/v1
-kind: ServiceMonitor
-metadata:
- labels:
- control-plane: controller-manager
- name: controller-manager-metrics-monitor
- namespace: system
-spec:
- endpoints:
- - path: /metrics
- port: https
- scheme: https
- bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
- tlsConfig:
- insecureSkipVerify: true
- selector:
- matchLabels:
- control-plane: controller-manager
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_client_clusterrole.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_client_clusterrole.yaml
deleted file mode 100644
index bd4af137a9f..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_client_clusterrole.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: metrics-reader
-rules:
-- nonResourceURLs: ["/metrics"]
- verbs: ["get"]
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_role.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_role.yaml
deleted file mode 100644
index 618f5e4177c..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_role.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: proxy-role
-rules:
-- apiGroups: ["authentication.k8s.io"]
- resources:
- - tokenreviews
- verbs: ["create"]
-- apiGroups: ["authorization.k8s.io"]
- resources:
- - subjectaccessreviews
- verbs: ["create"]
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_role_binding.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_role_binding.yaml
deleted file mode 100644
index 48ed1e4b85c..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_role_binding.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: proxy-rolebinding
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: proxy-role
-subjects:
-- kind: ServiceAccount
- name: default
- namespace: system
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_service.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_service.yaml
deleted file mode 100644
index 6cf656be149..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/auth_proxy_service.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- labels:
- control-plane: controller-manager
- name: controller-manager-metrics-service
- namespace: system
-spec:
- ports:
- - name: https
- port: 8443
- targetPort: https
- selector:
- control-plane: controller-manager
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml
deleted file mode 100644
index 19ab4dad440..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-# permissions for end users to edit cronjobs.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: cronjob-editor-role
-rules:
-- apiGroups:
- - batch.tutorial.kubebuilder.io
- resources:
- - cronjobs
- verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
-- apiGroups:
- - batch.tutorial.kubebuilder.io
- resources:
- - cronjobs/status
- verbs:
- - get
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml
deleted file mode 100644
index f31d815f94e..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-# permissions for end users to view cronjobs.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: cronjob-viewer-role
-rules:
-- apiGroups:
- - batch.tutorial.kubebuilder.io
- resources:
- - cronjobs
- verbs:
- - get
- - list
- - watch
-- apiGroups:
- - batch.tutorial.kubebuilder.io
- resources:
- - cronjobs/status
- verbs:
- - get
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/kustomization.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/kustomization.yaml
deleted file mode 100644
index 66c28338fe0..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/kustomization.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-resources:
-- role.yaml
-- role_binding.yaml
-- leader_election_role.yaml
-- leader_election_role_binding.yaml
-# Comment the following 4 lines if you want to disable
-# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
-# which protects your /metrics endpoint.
-- auth_proxy_service.yaml
-- auth_proxy_role.yaml
-- auth_proxy_role_binding.yaml
-- auth_proxy_client_clusterrole.yaml
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/leader_election_role.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/leader_election_role.yaml
deleted file mode 100644
index 6334cc51c83..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/leader_election_role.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-# permissions to do leader election.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: Role
-metadata:
- name: leader-election-role
-rules:
-- apiGroups:
- - ""
- - coordination.k8s.io
- resources:
- - configmaps
- - leases
- verbs:
- - get
- - list
- - watch
- - create
- - update
- - patch
- - delete
-- apiGroups:
- - ""
- resources:
- - events
- verbs:
- - create
- - patch
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
deleted file mode 100644
index eed16906f4d..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: RoleBinding
-metadata:
- name: leader-election-rolebinding
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: Role
- name: leader-election-role
-subjects:
-- kind: ServiceAccount
- name: default
- namespace: system
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/projectconfig_editor_role.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/projectconfig_editor_role.yaml
deleted file mode 100644
index 86a1b0ea4b5..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/projectconfig_editor_role.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-# permissions for end users to edit projectconfigs.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: projectconfig-editor-role
-rules:
-- apiGroups:
- - config.tutorial.kubebuilder.io
- resources:
- - projectconfigs
- verbs:
- - create
- - delete
- - get
- - list
- - patch
- - update
- - watch
-- apiGroups:
- - config.tutorial.kubebuilder.io
- resources:
- - projectconfigs/status
- verbs:
- - get
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/projectconfig_viewer_role.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/projectconfig_viewer_role.yaml
deleted file mode 100644
index 60f5abc8ef9..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/projectconfig_viewer_role.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-# permissions for end users to view projectconfigs.
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: projectconfig-viewer-role
-rules:
-- apiGroups:
- - config.tutorial.kubebuilder.io
- resources:
- - projectconfigs
- verbs:
- - get
- - list
- - watch
-- apiGroups:
- - config.tutorial.kubebuilder.io
- resources:
- - projectconfigs/status
- verbs:
- - get
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/role_binding.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/rbac/role_binding.yaml
deleted file mode 100644
index 8f2658702c8..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/rbac/role_binding.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: manager-rolebinding
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: manager-role
-subjects:
-- kind: ServiceAccount
- name: default
- namespace: system
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
deleted file mode 100644
index 3b425a01b14..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: batch.tutorial.kubebuilder.io/v1
-kind: CronJob
-metadata:
- name: cronjob-sample
-spec:
- # Add fields here
- foo: bar
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml
deleted file mode 100644
index 62f27fa4f73..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: batch.tutorial.kubebuilder.io/v2
-kind: CronJob
-metadata:
- name: cronjob-sample
-spec:
- # Add fields here
- foo: bar
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/samples/config_v2_projectconfig.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/samples/config_v2_projectconfig.yaml
deleted file mode 100644
index 00d1b793537..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/samples/config_v2_projectconfig.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: config.tutorial.kubebuilder.io/v2
-kind: ProjectConfig
-metadata:
- name: projectconfig-sample
-spec:
- # Add fields here
- foo: bar
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
deleted file mode 100644
index 25e21e3c963..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-# the following config is for teaching kustomize where to look at when substituting vars.
-# It requires kustomize v2.1.0 or newer to work properly.
-nameReference:
-- kind: Service
- version: v1
- fieldSpecs:
- - kind: MutatingWebhookConfiguration
- group: admissionregistration.k8s.io
- path: webhooks/clientConfig/service/name
- - kind: ValidatingWebhookConfiguration
- group: admissionregistration.k8s.io
- path: webhooks/clientConfig/service/name
-
-namespace:
-- kind: MutatingWebhookConfiguration
- group: admissionregistration.k8s.io
- path: webhooks/clientConfig/service/namespace
- create: true
-- kind: ValidatingWebhookConfiguration
- group: admissionregistration.k8s.io
- path: webhooks/clientConfig/service/namespace
- create: true
-
-varReference:
-- path: metadata/annotations
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/webhook/service.yaml b/docs/book/src/component-config-tutorial/testdata/project/config/webhook/service.yaml
deleted file mode 100644
index 31e0f829591..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/config/webhook/service.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-apiVersion: v1
-kind: Service
-metadata:
- name: webhook-service
- namespace: system
-spec:
- ports:
- - port: 443
- targetPort: 9443
- selector:
- control-plane: controller-manager
diff --git a/docs/book/src/component-config-tutorial/testdata/project/controllers/batch/cronjob_controller.go b/docs/book/src/component-config-tutorial/testdata/project/controllers/batch/cronjob_controller.go
deleted file mode 100644
index 979bdd2517d..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/controllers/batch/cronjob_controller.go
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package batch
-
-import (
- "context"
-
- "github.com/go-logr/logr"
- "k8s.io/apimachinery/pkg/runtime"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
-
- batchv1 "tutorial.kubebuilder.io/project/apis/batch/v1"
-)
-
-// CronJobReconciler reconciles a CronJob object
-type CronJobReconciler struct {
- client.Client
- Log logr.Logger
- Scheme *runtime.Scheme
-}
-
-// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
-// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
-
-// Reconcile is part of the main kubernetes reconciliation loop which aims to
-// move the current state of the cluster closer to the desired state.
-// TODO(user): Modify the Reconcile function to compare the state specified by
-// the CronJob object against the actual cluster state, and then
-// perform operations to make the cluster state reflect the state specified by
-// the user.
-//
-// For more details, check Reconcile and its Result here:
-// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.0-alpha.6/pkg/reconcile
-func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- _ = r.Log.WithValues("cronjob", req.NamespacedName)
-
- // your logic here
-
- return ctrl.Result{}, nil
-}
-
-// SetupWithManager sets up the controller with the Manager.
-func (r *CronJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
- return ctrl.NewControllerManagedBy(mgr).
- For(&batchv1.CronJob{}).
- Complete(r)
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/project/controllers/batch/suite_test.go b/docs/book/src/component-config-tutorial/testdata/project/controllers/batch/suite_test.go
deleted file mode 100644
index 5f692e46473..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/controllers/batch/suite_test.go
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package batch
-
-import (
- "path/filepath"
- "testing"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- "k8s.io/client-go/kubernetes/scheme"
- "k8s.io/client-go/rest"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-
- batchv1 "tutorial.kubebuilder.io/project/apis/batch/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 cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Controller Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
- }
-
- cfg, err := testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
-
- err = batchv1.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())
-
-}, 60)
-
-var _ = AfterSuite(func() {
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
diff --git a/docs/book/src/component-config-tutorial/testdata/project/go.mod b/docs/book/src/component-config-tutorial/testdata/project/go.mod
deleted file mode 100644
index 35be1c3a748..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/go.mod
+++ /dev/null
@@ -1,13 +0,0 @@
-module tutorial.kubebuilder.io/project
-
-go 1.15
-
-require (
- github.com/go-logr/logr v0.2.1
- github.com/onsi/ginkgo v1.14.1
- github.com/onsi/gomega v1.10.2
- k8s.io/api v0.19.2
- k8s.io/apimachinery v0.19.2
- k8s.io/client-go v0.19.2
- sigs.k8s.io/controller-runtime v0.7.0-alpha.6
-)
diff --git a/docs/book/src/component-config-tutorial/testdata/project/go.sum b/docs/book/src/component-config-tutorial/testdata/project/go.sum
deleted file mode 100644
index 559be72fdb6..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/go.sum
+++ /dev/null
@@ -1,638 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM=
-cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
-github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
-github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
-github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
-github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v0.2.1 h1:fV3MLmabKIZ383XifUjFSwcoGee0v9qgPp8wy5svibE=
-github.com/go-logr/logr v0.2.1/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4=
-github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU=
-github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
-github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
-github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
-github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
-github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
-github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
-github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
-github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
-github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
-github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
-github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
-github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
-github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
-github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
-github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
-github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8=
-go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
-go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
-go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
-go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k=
-gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms=
-k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
-k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA=
-k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
-k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
-k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
-k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
-k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc=
-k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
-k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
-k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
-k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
-k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
-k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
-k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
-sigs.k8s.io/controller-runtime v0.7.0-alpha.6 h1:ieFqEijQyDEZVIGwI5sYkk7VTa8Itim0kU/TCOnCkto=
-sigs.k8s.io/controller-runtime v0.7.0-alpha.6/go.mod h1:03b1n6EtlDvuBPPEOHadJUusruwLWgoT4BDCybMibnA=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/docs/book/src/component-config-tutorial/testdata/project/hack/boilerplate.go.txt b/docs/book/src/component-config-tutorial/testdata/project/hack/boilerplate.go.txt
deleted file mode 100644
index b75c7954b88..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/hack/boilerplate.go.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/testdata/project/main.go b/docs/book/src/component-config-tutorial/testdata/project/main.go
deleted file mode 100644
index 74dbec7a74b..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/project/main.go
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package main
-
-import (
- "flag"
- "os"
-
- // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
- // to ensure that exec-entrypoint and run can make use of them.
- _ "k8s.io/client-go/plugin/pkg/client/auth"
-
- "k8s.io/apimachinery/pkg/runtime"
- 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/log/zap"
-
- batchv1 "tutorial.kubebuilder.io/project/apis/batch/v1"
- batchv2 "tutorial.kubebuilder.io/project/apis/batch/v2"
- configv2 "tutorial.kubebuilder.io/project/apis/config/v2"
- batchcontrollers "tutorial.kubebuilder.io/project/controllers/batch"
- // +kubebuilder:scaffold:imports
-)
-
-var (
- scheme = runtime.NewScheme()
- setupLog = ctrl.Log.WithName("setup")
-)
-
-func init() {
- utilruntime.Must(clientgoscheme.AddToScheme(scheme))
-
- utilruntime.Must(batchv1.AddToScheme(scheme))
- utilruntime.Must(batchv2.AddToScheme(scheme))
- utilruntime.Must(configv2.AddToScheme(scheme))
- // +kubebuilder:scaffold:scheme
-}
-
-func main() {
- var configFile string
- flag.StringVar(&configFile, "config", "",
- "The controller will load its initial configuration from this file. "+
- "Omit this flag to use the default configuration values. "+
- "Command-line flags override configuration from this file.")
- opts := zap.Options{
- Development: true,
- }
- opts.BindFlags(flag.CommandLine)
- flag.Parse()
-
- ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
-
- var err error
- ctrlConfig := configv2.ProjectConfig{}
- options := ctrl.Options{Scheme: scheme}
- if configFile != "" {
- options, err = options.AndFrom(ctrl.ConfigFile().AtPath(configFile).OfKind(&ctrlConfig))
- if err != nil {
- setupLog.Error(err, "unable to load the config file")
- os.Exit(1)
- }
- }
-
- setupLog.Info("config loaded for", "cluster name", ctrlConfig.ClusterName)
-
- mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options)
- if err != nil {
- setupLog.Error(err, "unable to start manager")
- os.Exit(1)
- }
-
- if err = (&batchcontrollers.CronJobReconciler{
- Client: mgr.GetClient(),
- Log: ctrl.Log.WithName("controllers").WithName("batch").WithName("CronJob"),
- Scheme: mgr.GetScheme(),
- }).SetupWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create controller", "controller", "CronJob")
- os.Exit(1)
- }
- if err = (&batchv1.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
- os.Exit(1)
- }
- if err = (&batchv2.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
- os.Exit(1)
- }
- // +kubebuilder:scaffold:builder
-
- setupLog.Info("starting manager")
- if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
- setupLog.Error(err, "problem running manager")
- os.Exit(1)
- }
-}
diff --git a/docs/book/src/component-config-tutorial/testdata/projectconfig_types.go b/docs/book/src/component-config-tutorial/testdata/projectconfig_types.go
deleted file mode 100644
index 137015030ce..00000000000
--- a/docs/book/src/component-config-tutorial/testdata/projectconfig_types.go
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-Copyright 2020 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// +kubebuilder:docs-gen:collapse=Apache License
-
-/*
-We start out simply enough: we import the `config/v1alpha1` API group, which is
-exposed through ControllerRuntime.
-*/
-package v2
-
-import (
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- cfg "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
-)
-
-// +kubebuilder:object:root=true
-
-/*
-Next, we'll remove the default `ProjectConfigSpec` and `ProjectConfigList` then
-we'll embed `cfg.ControllerManagerConfigurationSpec` in `ProjectConfig`.
-*/
-
-// ProjectConfig is the Schema for the projectconfigs API
-type ProjectConfig struct {
- metav1.TypeMeta `json:",inline"`
-
- // ControllerManagerConfigurationSpec returns the contfigurations for controllers
- cfg.ControllerManagerConfigurationSpec `json:",inline"`
-
- ClusterName string `json:"clusterName,omitempty"`
-}
-
-/*
-If you haven't, you'll also need to remove the `ProjectConfigList` from the
-`SchemeBuilder.Register`.
-*/
-func init() {
- SchemeBuilder.Register(&ProjectConfig{})
-}
diff --git a/docs/book/src/component-config-tutorial/tutorial.md b/docs/book/src/component-config-tutorial/tutorial.md
deleted file mode 100644
index 78fab61d4b6..00000000000
--- a/docs/book/src/component-config-tutorial/tutorial.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Tutorial: ComponentConfig
-
-Nearly every project that is built for Kubernetes will eventually need to
-support passing in additional configurations into the controller. These could
-be to enable better logging, turn on/off specific feature gates, set the sync
-period, or a myriad of other controls. Previously this was commonly done using
-cli `flags` that your `main.go` would parse to make them accessible within your
-program. While this _works_ it's not a future forward design and the Kubernetes
-community has been migrating the core components away from this and toward
-using versioned config files, referred to as "component configs".
-
-The rest of this tutorial will show you how to configure your kubebuilder
-project with the a component config type then moves on to implementing a custom
-type so that you can extend this capability.
-
-
-
-
-Following Along vs Jumping Ahead
-
-Note that most of this tutorial is generated from literate Go files that
-form a runnable project, and live in the book source directory:
-[docs/book/src/componentconfig-tutorial/testdata/project][tutorial-source].
-
-[tutorial-source]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/docs/book/src/componentconfig-tutorial/testdata/project
-
-
-
-## Resources
-
-* [Versioned Component Configuration File Design](https://docs.google.com/document/d/1FdaEJUEh091qf5B98HM6_8MS764iXrxxigNIdwHYW9c/)
-
-* [Config v1alpha1 Go Docs](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/config/v1alpha1/)
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/updating-main.md b/docs/book/src/component-config-tutorial/updating-main.md
deleted file mode 100644
index 56a6772e8fd..00000000000
--- a/docs/book/src/component-config-tutorial/updating-main.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Updating main
-
-Once you have defined your new custom component config type we need to make
-sure our new config type has been imported and the types are registered with
-the scheme. _If you used `kubebuilder create api` this should have been
-automated._
-
-```go
-import (
- // ... other imports
- configv2 "tutorial.kubebuilder.io/project/apis/config/v2"
- // +kubebuilder:scaffold:imports
-)
-```
-With the package imported we can confirm the types have been added.
-
-```go
-func init() {
- // ... other scheme registrations
- utilruntime.Must(configv2.AddToScheme(scheme))
- // +kubebuilder:scaffold:scheme
-}
-```
-
-Lastly, we need to change the options parsing in
-`main.go` to use this new type. To do this we'll chain `OfKind` onto
-`ctrl.ConfigFile()` and pass in a pointer to the config kind.
-
-```go
-var err error
-ctrlConfig := configv2.ProjectConfig{}
-options := ctrl.Options{Scheme: scheme}
-if configFile != "" {
- options, err = options.AndFrom(ctrl.ConfigFile().AtPath(configFile).OfKind(&ctrlConfig))
- if err != nil {
- setupLog.Error(err, "unable to load the config file")
- os.Exit(1)
- }
-}
-```
-
-Now if you need to use the `.clusterName` field we defined in our custom kind
-you can call `ctrlConfig.ClusterName` which will be populated from the config
-file supplied.
\ No newline at end of file
diff --git a/docs/book/src/cronjob-tutorial/api-design.md b/docs/book/src/cronjob-tutorial/api-design.md
index 4f6069115cb..46c53a5ccec 100644
--- a/docs/book/src/cronjob-tutorial/api-design.md
+++ b/docs/book/src/cronjob-tutorial/api-design.md
@@ -17,7 +17,7 @@ machines. You've probably noticed them when specifying resources requests
and limits on pods in Kubernetes.
They conceptually work similar to floating point numbers: they have
-a significand, base, and exponent. Their serializable and human readable format
+a significant, base, and exponent. Their serializable and human readable format
uses whole numbers and suffixes to specify values much the way we describe
computer storage.
diff --git a/docs/book/src/cronjob-tutorial/basic-project.md b/docs/book/src/cronjob-tutorial/basic-project.md
index b850834cc3a..d333cecc3fd 100644
--- a/docs/book/src/cronjob-tutorial/basic-project.md
+++ b/docs/book/src/cronjob-tutorial/basic-project.md
@@ -7,7 +7,7 @@ basic pieces of boilerplate.
First up, basic infrastructure for building your project:
- `go.mod`: A new Go module matching our project, with
+go.mod: A new Go module matching our project, with
basic dependencies
```go
@@ -15,14 +15,14 @@ basic dependencies
```
-`Makefile`: Make targets for building and deploying your controller
+Makefile: Make targets for building and deploying your controller
```makefile
{{#include ./testdata/project/Makefile}}
```
-`PROJECT`: Kubebuilder metadata for scaffolding new components
+PROJECT: Kubebuilder metadata for scaffolding new components
```yaml
{{#include ./testdata/project/PROJECT}}
diff --git a/docs/book/src/cronjob-tutorial/cert-manager.md b/docs/book/src/cronjob-tutorial/cert-manager.md
index a8aed162d38..8fd0e4cbe21 100644
--- a/docs/book/src/cronjob-tutorial/cert-manager.md
+++ b/docs/book/src/cronjob-tutorial/cert-manager.md
@@ -1,24 +1,26 @@
-# Deploying the cert manager
+# Deploying cert-manager
-We suggest using [cert manager](https://github.com/jetstack/cert-manager) for
+We suggest using [cert-manager](https://github.com/cert-manager/cert-manager) for
provisioning the certificates for the webhook server. Other solutions should
also work as long as they put the certificates in the desired location.
You can follow
-[the cert manager documentation](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html)
+[the cert-manager documentation](https://cert-manager.io/docs/installation/)
to install it.
-Cert manager also has a component called CA injector, which is responsible for
-injecting the CA bundle into the Mutating|ValidatingWebhookConfiguration.
+cert-manager also has a component called [CA
+Injector](https://cert-manager.io/docs/concepts/ca-injector/), which is responsible for
+injecting the CA bundle into the [`MutatingWebhookConfiguration`](https://pkg.go.dev/k8s.io/api/admissionregistration/v1#MutatingWebhookConfiguration)
+/ [`ValidatingWebhookConfiguration`](https://pkg.go.dev/k8s.io/api/admissionregistration/v1#ValidatingWebhookConfiguration).
To accomplish that, you need to use an annotation with key
`cert-manager.io/inject-ca-from`
-in the Mutating|ValidatingWebhookConfiguration objects.
-The value of the annotation should point to an existing certificate CR instance
+in the [`MutatingWebhookConfiguration`](https://pkg.go.dev/k8s.io/api/admissionregistration/v1#MutatingWebhookConfiguration)
+/ [`ValidatingWebhookConfiguration`](https://pkg.go.dev/k8s.io/api/admissionregistration/v1#ValidatingWebhookConfiguration) objects.
+The value of the annotation should point to an existing [certificate request instance](https://cert-manager.io/docs/concepts/certificaterequest/)
in the format of `/`.
This is the [kustomize](https://github.com/kubernetes-sigs/kustomize) patch we
-used for annotating the Mutating|ValidatingWebhookConfiguration objects.
-```yaml
-{{#include ./testdata/project/config/default/webhookcainjection_patch.yaml}}
-```
+used for annotating the [`MutatingWebhookConfiguration`](https://pkg.go.dev/k8s.io/api/admissionregistration/v1#MutatingWebhookConfiguration)
+/ [`ValidatingWebhookConfiguration`](https://pkg.go.dev/k8s.io/api/admissionregistration/v1#ValidatingWebhookConfiguration) objects.
+
diff --git a/docs/book/src/cronjob-tutorial/controller-implementation.md b/docs/book/src/cronjob-tutorial/controller-implementation.md
index 95dc605f152..be548e069a1 100644
--- a/docs/book/src/cronjob-tutorial/controller-implementation.md
+++ b/docs/book/src/cronjob-tutorial/controller-implementation.md
@@ -18,7 +18,7 @@ The basic logic of our CronJob controller is this:
7. Requeue when we either see a running job (done automatically) or it's
time for the next scheduled run.
-{{#literatego ./testdata/project/controllers/cronjob_controller.go}}
+{{#literatego ./testdata/project/internal/controller/cronjob_controller.go}}
That was a doozy, but now we've got a working controller. Let's test
against the cluster, then, if we don't have any issues, deploy it!
diff --git a/docs/book/src/cronjob-tutorial/cronjob-tutorial.md b/docs/book/src/cronjob-tutorial/cronjob-tutorial.md
index ac57999bbfd..f87ae89d94c 100644
--- a/docs/book/src/cronjob-tutorial/cronjob-tutorial.md
+++ b/docs/book/src/cronjob-tutorial/cronjob-tutorial.md
@@ -9,7 +9,7 @@ building up to something pretty full-featured.
Let's pretend (and sure, this is a teensy bit contrived) that we've
finally gotten tired of the maintenance burden of the non-Kubebuilder
implementation of the CronJob controller in Kubernetes, and we'd like to
-rewrite it using KubeBuilder.
+rewrite it using Kubebuilder.
The job (no pun intended) of the *CronJob* controller is to run one-off
tasks on the Kubernetes cluster at regular intervals. It does this by
@@ -44,15 +44,34 @@ Kubebuilder](../quick-start.md#installation), then scaffold out a new
project:
```bash
+# create a project directory, and then run the init command.
+mkdir project
+cd project
# we'll use a domain of tutorial.kubebuilder.io,
# so all API groups will be .tutorial.kubebuilder.io.
-kubebuilder init --domain tutorial.kubebuilder.io
+kubebuilder init --domain tutorial.kubebuilder.io --repo tutorial.kubebuilder.io/project
```
+
Your project's name defaults to that of your current working directory.
You can pass `--project-name=` to set a different project name.
+
Now that we've got a project in place, let's take a look at what
Kubebuilder has scaffolded for us so far...
+
+
+
+Developing in $GOPATH
+
+If your project is initialized within [`GOPATH`][GOPATH-golang-docs], the implicitly called `go mod init` will interpolate the module path for you.
+Otherwise `--repo=` must be set.
+
+Read the [Go modules blogpost][go-modules-blogpost] if unfamiliar with the module system.
+
+
+
+[GOPATH-golang-docs]: https://golang.org/doc/code.html#GOPATH
+[go-modules-blogpost]: https://blog.golang.org/using-go-modules
\ No newline at end of file
diff --git a/docs/book/src/cronjob-tutorial/epilogue.md b/docs/book/src/cronjob-tutorial/epilogue.md
deleted file mode 100644
index 0e9be7c0264..00000000000
--- a/docs/book/src/cronjob-tutorial/epilogue.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Epilogue
-
-By this point, we've got a pretty full-featured implementation of the
-CronJob controller, made use of most of the features of
-KubeBuilder, and written tests for the controller using envtest.
-
-If you want more, head over to the [Multi-Version
-Tutorial](/multiversion-tutorial/tutorial.md) to learn how to add new API
-versions to a project.
-
-Additionally, you can try the following steps on your own -- we'll have
-a tutorial section on them Soon™:
-
-- adding [additional printer columns][printer-columns] `kubectl get`
-
-[printer-columns]: /reference/generating-crd.md#additional-printer-columns
diff --git a/docs/book/src/cronjob-tutorial/gvks.md b/docs/book/src/cronjob-tutorial/gvks.md
index b9cfd4b42ad..93fe17976b1 100644
--- a/docs/book/src/cronjob-tutorial/gvks.md
+++ b/docs/book/src/cronjob-tutorial/gvks.md
@@ -1,6 +1,6 @@
# Groups and Versions and Kinds, oh my!
-Actually, before we get started with our API, we should talk terminology
+Before we get started with our API, we should talk about terminology
a bit.
When we talk about APIs in Kubernetes, we often use 4 terms: *groups*,
@@ -38,7 +38,7 @@ lowercase form of the Kind.
## So, how does that correspond to Go?
-When we refer to a kind in a particular group-version, we'll call it
+When we refer to a kind in a particular group version, we'll call it
a *GroupVersionKind*, or GVK for short. Same with resources and GVR. As
we'll see shortly, each GVK corresponds to a given root Go type in
a package.
@@ -46,19 +46,20 @@ a package.
Now that we have our terminology straight, we can *actually* create our
API!
-## So, how can we create our API?
+## So, how can we create our API?
-In the next section, [Adding a new API](./cronjob-tutorial/new-api.html) we will check how the tool help us to create our own API's with the command `kubebuilder create api`.
+In the next section, [Adding a new API](../cronjob-tutorial/new-api.html), we will check how the tool helps us to
+create our own APIs with the command `kubebuilder create api`.
-The goal of this command is to create Custom Resource (CR) and Custom Resource Definition (CRD) for our Kind(s). To check it further see; [Extend the Kubernetes API with CustomResourceDefinitions][kubernetes-extend-api].
+The goal of this command is to create a Custom Resource (CR) and Custom Resource Definition (CRD) for our Kind(s). To check it further see; [Extend the Kubernetes API with CustomResourceDefinitions][kubernetes-extend-api].
## But, why create APIs at all?
-New APIs are how we teach Kubernetes about our custom objects. The Go structs are used to generate a Custom Resource Definition (CRD) which includes the schema for our data as well as tracking data like what our new type is called. We can then create instances of our custom objects which will be managed by our [controllers][controllers].
+New APIs are how we teach Kubernetes about our custom objects. The Go structs are used to generate a CRD which includes the schema for our data as well as tracking data like what our new type is called. We can then create instances of our custom objects which will be managed by our [controllers][controllers].
-Our APIs and resouces represent our solutions on the clusters. Basically, the CRDs are a definition of our customized Objects, and the CRs are an instance of it.
+Our APIs and resources represent our solutions on the clusters. Basically, the CRDs are a definition of our customized Objects, and the CRs are an instance of it.
-## Ah, do you have an example?
+## Ah, do you have an example?
Let’s think about the classic scenario where the goal is to have an application and its database running on the platform with Kubernetes. Then, one CRD could represent the App, and another one could represent the DB. By having one CRD to describe the App and another one for the DB, we will not be hurting concepts such as encapsulation, the single responsibility principle, and cohesion. Damaging these concepts could cause unexpected side effects, such as difficulty in extending, reuse, or maintenance, just to mention a few.
@@ -70,7 +71,7 @@ The `Scheme` we saw before is simply a way to keep track of what Go type
corresponds to a given GVK (don't be overwhelmed by its
[godocs](https://pkg.go.dev/k8s.io/apimachinery/pkg/runtime?tab=doc#Scheme)).
-For instance, suppose we mark that the
+For instance, suppose we mark the
`"tutorial.kubebuilder.io/api/v1".CronJob{}` type as being in the
`batch.tutorial.kubebuilder.io/v1` API group (implicitly saying it has the
Kind `CronJob`).
@@ -86,8 +87,8 @@ API server that says
}
```
-or properly look up the group-version when we go to submit a `&CronJob{}`
+or properly look up the group version when we go to submit a `&CronJob{}`
in an update.
[kubernetes-extend-api]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
-[controllers]: ../cronjob-tutorial/controller-overview.md
\ No newline at end of file
+[controllers]: ../cronjob-tutorial/controller-overview.md
diff --git a/docs/book/src/cronjob-tutorial/main-revisited.md b/docs/book/src/cronjob-tutorial/main-revisited.md
index 395b67e5bd3..4a8c0fd33dc 100644
--- a/docs/book/src/cronjob-tutorial/main-revisited.md
+++ b/docs/book/src/cronjob-tutorial/main-revisited.md
@@ -4,6 +4,6 @@ But first, remember how we said we'd [come back to `main.go`
again](/cronjob-tutorial/empty-main.md)? Let's take a look and see what's
changed, and what we need to add.
-{{#literatego ./testdata/project/main.go}}
+{{#literatego ./testdata/project/cmd/main.go}}
*Now* we can implement our controller.
diff --git a/docs/book/src/cronjob-tutorial/new-api.md b/docs/book/src/cronjob-tutorial/new-api.md
index e948f897aac..d15f1d4b57f 100644
--- a/docs/book/src/cronjob-tutorial/new-api.md
+++ b/docs/book/src/cronjob-tutorial/new-api.md
@@ -8,23 +8,11 @@ controller, we can use `kubebuilder create api`:
kubebuilder create api --group batch --version v1 --kind CronJob
```
+Press `y` for "Create Resource" and "Create Controller".
+
The first time we call this command for each group-version, it will create
a directory for the new group-version.
-
-
-Supporting older cluster versions
-
-The default CustomResourceDefinition manifests created alongside your Go API types
-use API version `v1`. If your project intends to support Kubernetes cluster versions older
-than v1.16, you must set `--crd-version v1beta1` and remove `preserveUnknownFields=false`
-from the `CRD_OPTIONS` Makefile variable.
-See the [CustomResourceDefinition generation reference][crd-reference] for details.
-
-[crd-reference]: /reference/generating-crd.md#supporting-older-cluster-versions
-
-
-
In this case, the
[`api/v1/`](https://sigs.k8s.io/kubebuilder/docs/book/src/cronjob-tutorial/testdata/project/api/v1)
directory is created, corresponding to the
diff --git a/docs/book/src/cronjob-tutorial/running-webhook.md b/docs/book/src/cronjob-tutorial/running-webhook.md
index 9275e53d9d6..456ffa01902 100644
--- a/docs/book/src/cronjob-tutorial/running-webhook.md
+++ b/docs/book/src/cronjob-tutorial/running-webhook.md
@@ -1,34 +1,33 @@
# Deploying Admission Webhooks
-## Kind Cluster
+## cert-manager
-It is recommended to develop your webhook with a
-[kind](../reference/kind.md) cluster for faster iteration.
-Why?
-
-- You can bring up a multi-node cluster locally within 1 minute.
-- You can tear it down in seconds.
-- You don't need to push your images to remote registry.
-
-## Cert Manager
-
-You need to follow [this](./cert-manager.md) to install the cert manager bundle.
+You need to follow [this](./cert-manager.md) to install the cert-manager bundle.
## Build your image
Run the following command to build your image locally.
```bash
-make docker-build
+make docker-build docker-push IMG=/:tag
```
-You don't need to push the image to a remote container registry if you are using
-a kind cluster. You can directly load your local image to your kind cluster:
+
+ Using Kind
+
+Consider incorporating Kind into your workflow for a faster, more efficient local development and CI experience.
+Note that, if you're using a Kind cluster, there's no need to push your image to a remote container registry.
+You can directly load your local image into your specified Kind cluster:
```bash
-kind load docker-image your-image-name:your-tag
+kind load docker-image :tag --name
```
+To know more, see: [Using Kind For Development Purposes and CI](./../reference/kind.md)
+
+
+
+
## Deploy Webhooks
You need to enable the webhook and cert manager configuration through kustomize.
@@ -38,13 +37,19 @@ You need to enable the webhook and cert manager configuration through kustomize.
{{#include ./testdata/project/config/default/kustomization.yaml}}
```
+And `config/crd/kustomization.yaml` should now look like the following:
+
+```yaml
+{{#include ./testdata/project/config/crd/kustomization.yaml}}
+```
+
Now you can deploy it to your cluster by
```bash
make deploy IMG=/:tag
```
-Wait a while til the webhook pod comes up and the certificates are provisioned.
+Wait a while till the webhook pod comes up and the certificates are provisioned.
It usually completes within 1 minute.
Now you can create a valid CronJob to test your webhooks. The creation should
@@ -57,17 +62,18 @@ kubectl create -f config/samples/batch_v1_cronjob.yaml
You can also try to create an invalid CronJob (e.g. use an ill-formatted
schedule field). You should see a creation failure with a validation error.
-
+
+The Bootstrapping Problem
-The Bootstrapping Problem
+When you deploy a webhook into the same cluster that it will validate, you can run into a *bootstrapping issue*:
+the webhook may try to validate the creation of its own Pod before it’s actually running.
+This can block the webhook from ever starting.
-If you are deploying a webhook for pods in the same cluster, be
-careful about the bootstrapping problem, since the creation request of the
-webhook pod would be sent to the webhook pod itself, which hasn't come up yet.
+To avoid this, make sure the webhook **ignores its own resources**.
+You can do this in one of two ways:
-To make it work, you can either use [namespaceSelector] if your kubernetes
-version is 1.9+ or use [objectSelector] if your kubernetes version is 1.15+ to
-skip itself.
+- **[namespaceSelector]** – label the namespace where the webhook runs and configure the webhook to skip it.
+- **[objectSelector]** – label the webhook’s own Pods or Deployments and exclude those objects directly.
diff --git a/docs/book/src/cronjob-tutorial/running.md b/docs/book/src/cronjob-tutorial/running.md
index 0b1dbde3c05..7e745c2c441 100644
--- a/docs/book/src/cronjob-tutorial/running.md
+++ b/docs/book/src/cronjob-tutorial/running.md
@@ -1,5 +1,12 @@
# Running and deploying the controller
+### Optional
+If opting to make any changes to the API definitions, then before proceeding,
+generate the manifests like CRs or CRDs with
+```bash
+make manifests
+```
+
To test out the controller, we can run it locally against the cluster.
Before we do so, though, we'll need to install our CRDs, as per the [quick
start](/quick-start.md). This will automatically update the YAML
@@ -9,11 +16,20 @@ manifests using controller-tools, if needed:
make install
```
+
+
+Too long annotations error
+
+If you encounter errors when applying the CRDs, due to `metadata.annotations` exceeding the
+262144 bytes limit, please refer to the specific entry in the [FAQ section](/faq#the-error-too-long-must-have-at-most-262144-bytes-is-faced-when-i-run-make-install-to-apply-the-crd-manifests-how-to-solve-it-why-this-error-is-faced).
+
+
+
Now that we've installed our CRDs, we can run the controller against our
cluster. This will use whatever credentials that we connect to the
cluster with, so we don't need to worry about RBAC just yet.
-
+
Running webhooks locally
@@ -32,7 +48,8 @@ your local code-run-test cycle, as we do below.
In a separate terminal, run
```bash
-make run ENABLE_WEBHOOKS=false
+export ENABLE_WEBHOOKS=false
+make run
```
You should see logs from the controller about starting up, but it won't do
@@ -65,5 +82,30 @@ make docker-build docker-push IMG=/:tag
make deploy IMG=/:tag
```
+
+Registry Permission
+
+This image ought to be published in the personal registry you specified. And it is required to have access to pull the image from the working environment.
+Make sure you have the proper permission to the registry if the above commands don't work.
+
+Consider incorporating Kind into your workflow for a faster, more efficient local development and CI experience.
+Note that, if you're using a Kind cluster, there's no need to push your image to a remote container registry.
+You can directly load your local image into your specified Kind cluster:
+
+```bash
+kind load docker-image :tag --name
+```
+
+To know more, see: [Using Kind For Development Purposes and CI](./../reference/kind.md)
+
+RBAC errors
+
+If you encounter RBAC errors, you may need to grant yourself cluster-admin
+privileges or be logged in as admin. See [Prerequisites for using Kubernetes RBAC on GKE cluster v1.11.x and older][pre-rbc-gke] which may be your case.
+
+
+
If we list cronjobs again like we did before, we should see the controller
functioning again!
+
+[pre-rbc-gke]: https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control#iam-rolebinding-bootstrap
\ No newline at end of file
diff --git a/docs/book/src/cronjob-tutorial/testdata/emptyapi.go b/docs/book/src/cronjob-tutorial/testdata/emptyapi.go
index 9b26ffd8114..5afe50a6d14 100644
--- a/docs/book/src/cronjob-tutorial/testdata/emptyapi.go
+++ b/docs/book/src/cronjob-tutorial/testdata/emptyapi.go
@@ -1,4 +1,5 @@
/*
+Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -68,8 +69,8 @@ a Kind. Then, the `object` generator generates an implementation of the
interface that all types representing Kinds must implement.
*/
-//+kubebuilder:object:root=true
-//+kubebuilder:subresource:status
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
// CronJob is the Schema for the cronjobs API
type CronJob struct {
@@ -80,7 +81,7 @@ type CronJob struct {
Status CronJobStatus `json:"status,omitempty"`
}
-//+kubebuilder:object:root=true
+// +kubebuilder:object:root=true
// CronJobList contains a list of CronJob
type CronJobList struct {
diff --git a/docs/book/src/cronjob-tutorial/testdata/emptycontroller.go b/docs/book/src/cronjob-tutorial/testdata/emptycontroller.go
index f1b723e4432..ee0db87cc3e 100644
--- a/docs/book/src/cronjob-tutorial/testdata/emptycontroller.go
+++ b/docs/book/src/cronjob-tutorial/testdata/emptycontroller.go
@@ -1,4 +1,5 @@
/*
+Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -24,10 +25,10 @@ package controllers
import (
"context"
- "github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
batchv1 "tutorial.kubebuilder.io/project/api/v1"
)
@@ -41,7 +42,6 @@ objects, so these are added out of the box.
// CronJobReconciler reconciles a CronJob object
type CronJobReconciler struct {
client.Client
- Log logr.Logger
Scheme *runtime.Scheme
}
@@ -55,6 +55,16 @@ needed to run. As we add more functionality, we'll need to revisit these.
// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
+/*
+The `ClusterRole` manifest at `config/rbac/role.yaml` is generated from the above markers via controller-gen with the following command:
+*/
+
+// make manifests
+
+/*
+NOTE: If you receive an error, please run the specified command in the error and re-run `make manifests`.
+*/
+
/*
`Reconcile` actually performs the reconciling for a single named object.
Our [Request](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile?tab=doc#Request) just has a name, but we can use the client to fetch
@@ -66,7 +76,7 @@ some changes.
Most controllers need a logging handle and a context, so we set them up here.
-The [context](https://golang.org/pkg/context/) is used to allow cancelation of
+The [context](https://golang.org/pkg/context/) is used to allow cancellation of
requests, and potentially things like tracing. It's the first argument to all
client methods. The `Background` context is just a basic context without any
extra data or timing restrictions.
@@ -78,7 +88,7 @@ some pairs at the top of our reconcile method to have those attached to all log
lines in this reconciler.
*/
func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- _ = r.Log.WithValues("cronjob", req.NamespacedName)
+ _ = logf.FromContext(ctx)
// your logic here
diff --git a/docs/book/src/cronjob-tutorial/testdata/emptymain.go b/docs/book/src/cronjob-tutorial/testdata/emptymain.go
index c06646254e1..5bcdf964549 100644
--- a/docs/book/src/cronjob-tutorial/testdata/emptymain.go
+++ b/docs/book/src/cronjob-tutorial/testdata/emptymain.go
@@ -1,4 +1,5 @@
/*
+Copyright 2022 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.
@@ -19,7 +20,7 @@ limitations under the License.
Our package starts out with some basic imports. Particularly:
- The core [controller-runtime](https://pkg.go.dev/sigs.k8s.io/controller-runtime?tab=doc) library
-- The default controller-runtime logging, Zap (more on that a bit later)
+- The default controller-runtime logging, [Zap](https://pkg.go.dev/go.uber.org/zap) (more on that a bit later)
*/
@@ -27,7 +28,6 @@ package main
import (
"flag"
- "fmt"
"os"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
@@ -42,6 +42,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
// +kubebuilder:scaffold:imports
)
@@ -60,7 +62,7 @@ var (
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
- //+kubebuilder:scaffold:scheme
+ // +kubebuilder:scaffold:scheme
}
/*
@@ -103,9 +105,11 @@ func main() {
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- MetricsBindAddress: metricsAddr,
- Port: 9443,
+ Scheme: scheme,
+ Metrics: server.Options{
+ BindAddress: metricsAddr,
+ },
+ WebhookServer: webhook.NewServer(webhook.Options{Port: 9443}),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "80807133.tutorial.kubebuilder.io",
@@ -116,42 +120,58 @@ func main() {
}
/*
- Note that the Manager can restrict the namespace that all controllers will watch for resources by:
+ Note that the `Manager` can restrict the namespace that all controllers will watch for resources by:
*/
- mgr, err = ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- Namespace: namespace,
- MetricsBindAddress: metricsAddr,
- Port: 9443,
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ Cache: cache.Options{
+ DefaultNamespaces: map[string]cache.Config{
+ namespace: {},
+ },
+ },
+ Metrics: server.Options{
+ BindAddress: metricsAddr,
+ },
+ WebhookServer: webhook.NewServer(webhook.Options{Port: 9443}),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "80807133.tutorial.kubebuilder.io",
})
/*
- The above example will change the scope of your project to a single Namespace. In this scenario,
+ 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/).
+ `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/).
- Also, it is possible to use the MultiNamespacedCacheBuilder to watch a specific set of namespaces:
+ 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:
*/
var namespaces []string // List of Namespaces
+ defaultNamespaces := make(map[string]cache.Config)
+
+ for _, ns := range namespaces {
+ defaultNamespaces[ns] = cache.Config{}
+ }
- mgr, err = ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- NewCache: cache.MultiNamespacedCacheBuilder(namespaces),
- MetricsBindAddress: fmt.Sprintf("%s:%d", metricsHost, metricsPort),
- Port: 9443,
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ Cache: cache.Options{
+ DefaultNamespaces: defaultNamespaces,
+ },
+ Metrics: server.Options{
+ BindAddress: metricsAddr,
+ },
+ WebhookServer: webhook.NewServer(webhook.Options{Port: 9443}),
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "80807133.tutorial.kubebuilder.io",
})
/*
- For further information see [MultiNamespacedCacheBuilder](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/cache?tab=doc#MultiNamespacedCacheBuilder)
+ For further information see [`cache.Options{}`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/cache#Options)
*/
// +kubebuilder:scaffold:builder
diff --git a/docs/book/src/cronjob-tutorial/testdata/finalizer_example.go b/docs/book/src/cronjob-tutorial/testdata/finalizer_example.go
index 5676519e0f5..89296075e73 100644
--- a/docs/book/src/cronjob-tutorial/testdata/finalizer_example.go
+++ b/docs/book/src/cronjob-tutorial/testdata/finalizer_example.go
@@ -38,9 +38,9 @@ import (
By default, kubebuilder will include the RBAC rules necessary to update finalizers for CronJobs.
*/
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
/*
The code snippet below shows skeleton code for implementing a finalizer.
@@ -49,7 +49,7 @@ The code snippet below shows skeleton code for implementing a finalizer.
func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("cronjob", req.NamespacedName)
- var cronJob *batchv1.CronJob
+ cronJob := &batchv1.CronJob{}
if err := r.Get(ctx, req.NamespacedName, cronJob); err != nil {
log.Error(err, "unable to fetch CronJob")
// we'll ignore not-found errors, since they can't be fixed by an immediate
@@ -64,9 +64,9 @@ func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
// examine DeletionTimestamp to determine if object is under deletion
if cronJob.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
- // then lets add the finalizer and update the object. This is equivalent
- // registering our finalizer.
- if !containsString(cronJob.GetFinalizers(), myFinalizerName) {
+ // then let's add the finalizer and update the object. This is equivalent
+ // to registering our finalizer.
+ if !controllerutil.ContainsFinalizer(cronJob, myFinalizerName) {
controllerutil.AddFinalizer(cronJob, myFinalizerName)
if err := r.Update(ctx, cronJob); err != nil {
return ctrl.Result{}, err
@@ -74,11 +74,11 @@ func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
} else {
// The object is being deleted
- if containsString(cronJob.GetFinalizers(), myFinalizerName) {
- // our finalizer is present, so lets handle any external dependency
+ if controllerutil.ContainsFinalizer(cronJob, myFinalizerName) {
+ // our finalizer is present, so let's handle any external dependency
if err := r.deleteExternalResources(cronJob); err != nil {
// if fail to delete the external dependency here, return with error
- // so that it can be retried
+ // so that it can be retried.
return ctrl.Result{}, err
}
@@ -105,23 +105,3 @@ func (r *Reconciler) deleteExternalResources(cronJob *batch.CronJob) error {
// Ensure that delete implementation is idempotent and safe to invoke
// multiple times for same object.
}
-
-// Helper functions to check and remove string from a slice of strings.
-func containsString(slice []string, s string) bool {
- for _, item := range slice {
- if item == s {
- return true
- }
- }
- return false
-}
-
-func removeString(slice []string, s string) (result []string) {
- for _, item := range slice {
- if item == s {
- continue
- }
- result = append(result, item)
- }
- return
-}
diff --git a/docs/book/src/cronjob-tutorial/testdata/generate_cronjob.sh b/docs/book/src/cronjob-tutorial/testdata/generate_cronjob.sh
index 9c8061d68d3..2adc6603cef 100755
--- a/docs/book/src/cronjob-tutorial/testdata/generate_cronjob.sh
+++ b/docs/book/src/cronjob-tutorial/testdata/generate_cronjob.sh
@@ -52,9 +52,11 @@ function gen_cronjob_tutorial {
mkdir project
cd project
header_text "creating tutorial.kubebuilder.io base ..."
- kubebuilder init --domain=tutorial.kubebuilder.io --project-version=3-alpha --repo=tutorial.kubebuilder.io/project --license apache2 --owner "The Kubernetes authors"
+ kubebuilder init --plugins=go/v4 --domain tutorial.kubebuilder.io --repo tutorial.kubebuilder.io/project --license apache2 --owner "The Kubernetes authors"
kubebuilder create api --group batch --version v1 --kind CronJob --resource --controller
kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
+ go mod tidy
+ make
}
gen_cronjob_tutorial
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json b/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json
new file mode 100644
index 00000000000..a3ab7541cb6
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/devcontainer.json
@@ -0,0 +1,25 @@
+{
+ "name": "Kubebuilder DevContainer",
+ "image": "golang:1.24",
+ "features": {
+ "ghcr.io/devcontainers/features/docker-in-docker:2": {},
+ "ghcr.io/devcontainers/features/git:1": {}
+ },
+
+ "runArgs": ["--network=host"],
+
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.shell.linux": "/bin/bash"
+ },
+ "extensions": [
+ "ms-kubernetes-tools.vscode-kubernetes-tools",
+ "ms-azuretools.vscode-docker"
+ ]
+ }
+ },
+
+ "onCreateCommand": "bash .devcontainer/post-install.sh"
+}
+
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/post-install.sh b/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/post-install.sh
new file mode 100644
index 00000000000..67f3e97ab32
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.devcontainer/post-install.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -x
+
+curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-$(go env GOARCH)
+chmod +x ./kind
+mv ./kind /usr/local/bin/kind
+
+curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/$(go env GOARCH)
+chmod +x kubebuilder
+mv kubebuilder /usr/local/bin/
+
+KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
+curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/$(go env GOARCH)/kubectl"
+chmod +x kubectl
+mv kubectl /usr/local/bin/kubectl
+
+docker network create -d=bridge --subnet=172.19.0.0/24 kind
+
+kind version
+kubebuilder version
+docker --version
+go version
+kubectl version --client
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.dockerignore b/docs/book/src/cronjob-tutorial/testdata/project/.dockerignore
index 243f81a5080..9af8280793c 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/.dockerignore
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.dockerignore
@@ -1,5 +1,11 @@
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
-# Ignore all files which are not go type
+# Ignore everything by default and re-include only needed files
+**
+
+# Re-include Go source files (but not *_test.go)
!**/*.go
-!**/*.mod
-!**/*.sum
+**/*_test.go
+
+# Re-include Go module files
+!go.mod
+!go.sum
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
new file mode 100644
index 00000000000..029218b7232
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/lint.yml
@@ -0,0 +1,23 @@
+name: Lint
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ lint:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+
+ - name: Run linter
+ uses: golangci/golangci-lint-action@v8
+ with:
+ version: v2.6.0
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-chart.yml b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-chart.yml
new file mode 100644
index 00000000000..cc3070bd7a0
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-chart.yml
@@ -0,0 +1,74 @@
+name: Test Chart
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test-e2e:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ 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-$(go env GOARCH)
+ 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
+ run: |
+ go mod tidy
+ make docker-build IMG=project:v0.1.0
+ kind load docker-image project: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
+ run: |
+ helm lint ./dist/chart
+
+
+ - name: Install cert-manager via Helm (wait for readiness)
+ 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 crds.enabled=true \
+ --wait \
+ --timeout 300s
+
+# TODO: Uncomment if Prometheus is enabled
+# - name: Install Prometheus Operator CRDs
+# run: |
+# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
+# helm repo update
+# helm install prometheus-crds prometheus-community/prometheus-operator-crds
+
+ - name: Install Helm chart for project
+ run: |
+ helm install my-release ./dist/chart --create-namespace --namespace project-system
+
+ - name: Check Helm release status
+ run: |
+ helm status my-release --namespace project-system
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
new file mode 100644
index 00000000000..4cdfb30e026
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test-e2e.yml
@@ -0,0 +1,32 @@
+name: E2E Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test-e2e:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ 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-$(go env GOARCH)
+ chmod +x ./kind
+ sudo mv ./kind /usr/local/bin/kind
+
+ - name: Verify kind installation
+ run: kind version
+
+ - name: Running Test e2e
+ run: |
+ go mod tidy
+ make test-e2e
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
new file mode 100644
index 00000000000..fc2e80d304d
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+
+ - name: Running Tests
+ run: |
+ go mod tidy
+ make test
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.gitignore b/docs/book/src/cronjob-tutorial/testdata/project/.gitignore
index c0a7a54cac5..9f0f3a1c673 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/.gitignore
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.gitignore
@@ -1,25 +1,30 @@
-
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
-bin
-testbin/*
+bin/*
+Dockerfile.cross
-# Test binary, build with `go test -c`
+# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-# Kubernetes Generated files - skip generated files, except for vendored files
+# Go workspace file
+go.work
+# Kubernetes Generated files - skip generated files, except for vendored files
!vendor/**/zz_generated.*
# editor and IDE paraphernalia
.idea
+.vscode
*.swp
*.swo
*~
+
+# Kubeconfig might contain secrets
+*.kubeconfig
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/.golangci.yml b/docs/book/src/cronjob-tutorial/testdata/project/.golangci.yml
new file mode 100644
index 00000000000..e5b21b0f11c
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/.golangci.yml
@@ -0,0 +1,52 @@
+version: "2"
+run:
+ allow-parallel-runners: true
+linters:
+ default: none
+ enable:
+ - copyloopvar
+ - dupl
+ - errcheck
+ - ginkgolinter
+ - goconst
+ - gocyclo
+ - govet
+ - ineffassign
+ - lll
+ - misspell
+ - nakedret
+ - prealloc
+ - revive
+ - staticcheck
+ - unconvert
+ - unparam
+ - unused
+ settings:
+ revive:
+ rules:
+ - name: comment-spacings
+ - name: import-shadowing
+ exclusions:
+ generated: lax
+ rules:
+ - linters:
+ - lll
+ path: api/*
+ - linters:
+ - dupl
+ - lll
+ path: internal/*
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
+formatters:
+ enable:
+ - gofmt
+ - goimports
+ exclusions:
+ generated: lax
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile b/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile
index ce816f3b0e7..6466c484903 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile
+++ b/docs/book/src/cronjob-tutorial/testdata/project/Dockerfile
@@ -1,5 +1,7 @@
# Build the manager binary
-FROM golang:1.15 as builder
+FROM golang:1.24 AS builder
+ARG TARGETOS
+ARG TARGETARCH
WORKDIR /workspace
# Copy the Go Modules manifests
@@ -9,13 +11,15 @@ COPY go.sum go.sum
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
-# Copy the go source
-COPY main.go main.go
-COPY api/ api/
-COPY controllers/ controllers/
+# Copy the Go source (relies on .dockerignore to filter)
+COPY . .
# Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
+# the GOARCH has no default value to allow the binary to be built according to the host where the command
+# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
+# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
+# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/Makefile b/docs/book/src/cronjob-tutorial/testdata/project/Makefile
index 79c2a4b645b..838c45f3ec1 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/Makefile
+++ b/docs/book/src/cronjob-tutorial/testdata/project/Makefile
@@ -1,8 +1,5 @@
-
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
-# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
-CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
@@ -11,19 +8,25 @@ else
GOBIN=$(shell go env GOBIN)
endif
+# CONTAINER_TOOL defines the container tool to be used for building images.
+# Be aware that the target commands are only tested with Docker which is
+# scaffolded by default. However, you might want to replace it to use other
+# tools. (i.e. podman)
+CONTAINER_TOOL ?= docker
+
# Setting SHELL to bash allows bash commands to be executed by recipes.
-# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
+.PHONY: all
all: build
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
-# target descriptions by '##'. The awk commands is responsible for reading the
+# target descriptions by '##'. The awk command is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
@@ -32,77 +35,220 @@ all: build
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
+.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Development
+.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
- $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+ # Note that the option maxDescLen=0 was added in the default scaffold in order to sort out the issue
+ # Too long: must have at most 262144 bytes. By using kubectl apply to create / update resources an annotation
+ # is created by K8s API to store the latest version of the resource ( kubectl.kubernetes.io/last-applied-configuration).
+ # However, it has a size limit and if the CRD is too big with so many long descriptions as this one it will cause the failure.
+ "$(CONTROLLER_GEN)" rbac:roleName=manager-role crd:maxDescLen=0 webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
- $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
+ "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt" paths="./..."
+.PHONY: fmt
fmt: ## Run go fmt against code.
go fmt ./...
+.PHONY: vet
vet: ## Run go vet against code.
go vet ./...
-ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
-test: manifests generate fmt vet ## Run tests.
- mkdir -p ${ENVTEST_ASSETS_DIR}
- test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.2/hack/setup-envtest.sh
- source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
+.PHONY: test
+test: manifests generate fmt vet setup-envtest ## Run tests.
+ KUBEBUILDER_ASSETS="$(shell "$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
+
+# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
+# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
+# CertManager is installed by default; skip with:
+# - CERT_MANAGER_INSTALL_SKIP=true
+KIND_CLUSTER ?= project-test-e2e
+
+.PHONY: setup-test-e2e
+setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
+ @command -v $(KIND) >/dev/null 2>&1 || { \
+ echo "Kind is not installed. Please install Kind manually."; \
+ exit 1; \
+ }
+ @case "$$($(KIND) get clusters)" in \
+ *"$(KIND_CLUSTER)"*) \
+ echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
+ *) \
+ echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
+ $(KIND) create cluster --name $(KIND_CLUSTER) ;; \
+ esac
+
+.PHONY: test-e2e
+test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
+ KIND=$(KIND) KIND_CLUSTER=$(KIND_CLUSTER) go test -tags=e2e ./test/e2e/ -v -ginkgo.v
+ $(MAKE) cleanup-test-e2e
+
+.PHONY: cleanup-test-e2e
+cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
+ @$(KIND) delete cluster --name $(KIND_CLUSTER)
+
+.PHONY: lint
+lint: golangci-lint ## Run golangci-lint linter
+ "$(GOLANGCI_LINT)" run
+
+.PHONY: lint-fix
+lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
+ "$(GOLANGCI_LINT)" run --fix
+
+.PHONY: lint-config
+lint-config: golangci-lint ## Verify golangci-lint linter configuration
+ "$(GOLANGCI_LINT)" config verify
##@ Build
-build: generate fmt vet ## Build manager binary.
- go build -o bin/manager main.go
+.PHONY: build
+build: manifests generate fmt vet ## Build manager binary.
+ go build -o bin/manager cmd/main.go
+.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
- go run ./main.go
+ go run ./cmd/main.go
-docker-build: test ## Build docker image with the manager.
- docker build -t ${IMG} .
+# If you wish to build the manager image targeting other platforms you can use the --platform flag.
+# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
+# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+.PHONY: docker-build
+docker-build: ## Build docker image with the manager.
+ $(CONTAINER_TOOL) build -t ${IMG} .
+.PHONY: docker-push
docker-push: ## Push docker image with the manager.
- docker push ${IMG}
+ $(CONTAINER_TOOL) push ${IMG}
+
+# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
+# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
+# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
+# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail)
+# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
+PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
+.PHONY: docker-buildx
+docker-buildx: ## Build and push docker image for the manager for cross-platform support
+ # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
+ sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
+ - $(CONTAINER_TOOL) buildx create --name project-builder
+ $(CONTAINER_TOOL) buildx use project-builder
+ - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
+ - $(CONTAINER_TOOL) buildx rm project-builder
+ rm Dockerfile.cross
+
+.PHONY: build-installer
+build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
+ mkdir -p dist
+ cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG}
+ "$(KUSTOMIZE)" build config/default > dist/install.yaml
##@ Deployment
+ifndef ignore-not-found
+ ignore-not-found = false
+endif
+
+.PHONY: install
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/crd | kubectl apply -f -
+ @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
+ if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" apply -f -; else echo "No CRDs to install; skipping."; fi
-uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/crd | kubectl delete -f -
+.PHONY: uninstall
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+ @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
+ if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -; else echo "No CRDs to delete; skipping."; fi
+.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
- cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
- $(KUSTOMIZE) build config/default | kubectl apply -f -
-
-undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/default | kubectl delete -f -
-
-
-CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
-controller-gen: ## Download controller-gen locally if necessary.
- $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
-
-KUSTOMIZE = $(shell pwd)/bin/kustomize
-kustomize: ## Download kustomize locally if necessary.
- $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
-
-# go-get-tool will 'go get' any package $2 and install it to $1.
-PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
-define go-get-tool
-@[ -f $(1) ] || { \
-set -e ;\
-TMP_DIR=$$(mktemp -d) ;\
-cd $$TMP_DIR ;\
-go mod init tmp ;\
-echo "Downloading $(2)" ;\
-GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
-rm -rf $$TMP_DIR ;\
-}
+ cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG}
+ "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" apply -f -
+
+.PHONY: undeploy
+undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+ "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -
+
+##@ Dependencies
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+ mkdir -p "$(LOCALBIN)"
+
+## Tool Binaries
+KUBECTL ?= kubectl
+KIND ?= kind
+KUSTOMIZE ?= $(LOCALBIN)/kustomize
+CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
+
+## Tool Versions
+KUSTOMIZE_VERSION ?= v5.7.1
+CONTROLLER_TOOLS_VERSION ?= v0.19.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 v='$(call gomodver,sigs.k8s.io/controller-runtime)'; \
+ [ -n "$$v" ] || { echo "Set ENVTEST_VERSION manually (controller-runtime replace has no tag)" >&2; exit 1; }; \
+ printf '%s\n' "$$v" | sed -E 's/^v?([0-9]+)\.([0-9]+).*/release-\1.\2/')
+
+#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
+ENVTEST_K8S_VERSION ?= $(shell v='$(call gomodver,k8s.io/api)'; \
+ [ -n "$$v" ] || { echo "Set ENVTEST_K8S_VERSION manually (k8s.io/api replace has no tag)" >&2; exit 1; }; \
+ printf '%s\n' "$$v" | sed -E 's/^v?[0-9]+\.([0-9]+).*/1.\1/')
+
+GOLANGCI_LINT_VERSION ?= v2.6.0
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
+$(KUSTOMIZE): $(LOCALBIN)
+ $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
+
+.PHONY: controller-gen
+controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
+$(CONTROLLER_GEN): $(LOCALBIN)
+ $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
+
+.PHONY: setup-envtest
+setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
+ @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..."
+ @"$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path || { \
+ echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \
+ exit 1; \
+ }
+
+.PHONY: envtest
+envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
+$(ENVTEST): $(LOCALBIN)
+ $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
+
+.PHONY: golangci-lint
+golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
+$(GOLANGCI_LINT): $(LOCALBIN)
+ $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
+
+# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
+# $1 - target path with name of binary
+# $2 - package url which can be installed
+# $3 - specific version of package
+define go-install-tool
+@[ -f "$(1)-$(3)" ] && [ "$$(readlink -- "$(1)" 2>/dev/null)" = "$(1)-$(3)" ] || { \
+set -e; \
+package=$(2)@$(3) ;\
+echo "Downloading $${package}" ;\
+rm -f "$(1)" ;\
+GOBIN="$(LOCALBIN)" go install $${package} ;\
+mv "$(LOCALBIN)/$$(basename "$(1)")" "$(1)-$(3)" ;\
+} ;\
+ln -sf "$$(realpath "$(1)-$(3)")" "$(1)"
+endef
+
+define gomodver
+$(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' $(1) 2>/dev/null)
endef
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/PROJECT b/docs/book/src/cronjob-tutorial/testdata/project/PROJECT
index 089d9117b62..69670a776ef 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/PROJECT
+++ b/docs/book/src/cronjob-tutorial/testdata/project/PROJECT
@@ -1,6 +1,15 @@
+# Code generated by tool. DO NOT EDIT.
+# This file is used to track the info used to scaffold your project
+# and allow the plugins properly work.
+# More info: https://book.kubebuilder.io/reference/project-config.html
+cliVersion: (devel)
domain: tutorial.kubebuilder.io
layout:
-- go.kubebuilder.io/v3
+- go.kubebuilder.io/v4
+plugins:
+ helm.kubebuilder.io/v2-alpha:
+ manifests: dist/install.yaml
+ output: dist
projectName: project
repo: tutorial.kubebuilder.io/project
resources:
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/README.md b/docs/book/src/cronjob-tutorial/testdata/project/README.md
new file mode 100644
index 00000000000..fcc53b94123
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/README.md
@@ -0,0 +1,135 @@
+# project
+// TODO(user): Add simple overview of use/purpose
+
+## Description
+// TODO(user): An in-depth paragraph about your project and overview of use
+
+## Getting Started
+
+### Prerequisites
+- go version v1.24.6+
+- docker version 17.03+.
+- kubectl version v1.11.3+.
+- Access to a Kubernetes v1.11.3+ cluster.
+
+### To Deploy on the cluster
+**Build and push your image to the location specified by `IMG`:**
+
+```sh
+make docker-build docker-push IMG=/project:tag
+```
+
+**NOTE:** This image ought to be published in the personal registry you specified.
+And it is required to have access to pull the image from the working environment.
+Make sure you have the proper permission to the registry if the above commands don’t work.
+
+**Install the CRDs into the cluster:**
+
+```sh
+make install
+```
+
+**Deploy the Manager to the cluster with the image specified by `IMG`:**
+
+```sh
+make deploy IMG=/project:tag
+```
+
+> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin
+privileges or be logged in as admin.
+
+**Create instances of your solution**
+You can apply the samples (examples) from the config/sample:
+
+```sh
+kubectl apply -k config/samples/
+```
+
+>**NOTE**: Ensure that the samples has default values to test it out.
+
+### To Uninstall
+**Delete the instances (CRs) from the cluster:**
+
+```sh
+kubectl delete -k config/samples/
+```
+
+**Delete the APIs(CRDs) from the cluster:**
+
+```sh
+make uninstall
+```
+
+**UnDeploy the controller from the cluster:**
+
+```sh
+make undeploy
+```
+
+## Project Distribution
+
+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:
+
+```sh
+make build-installer IMG=/project:tag
+```
+
+**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.
+
+2. Using the installer
+
+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/v2-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
+
+**NOTE:** Run `make help` for more information on all potential `make` targets
+
+More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
+
+## License
+
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under 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 80c23ea9ba9..edf2e16ea6c 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 2021 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.
@@ -17,12 +17,14 @@ limitations under the License.
/*
*/
+
package v1
/*
*/
+
import (
- batchv1beta1 "k8s.io/api/batch/v1beta1"
+ batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -33,83 +35,82 @@ import (
// +kubebuilder:docs-gen:collapse=Imports
/*
-First, let's take a look at our spec. As we discussed before, spec holds
-*desired state*, so any "inputs" to our controller go here.
+ First, let's take a look at our spec. As we discussed before, spec holds
+ *desired state*, so any "inputs" to our controller go here.
-Fundamentally a CronJob needs the following pieces:
+ Fundamentally a CronJob needs the following pieces:
-- A schedule (the *cron* in CronJob)
-- A template for the Job to run (the
-*job* in CronJob)
+ - A schedule (the *cron* in CronJob)
+ - A template for the Job to run (the
+ *job* in CronJob)
-We'll also want a few extras, which will make our users' lives easier:
+ We'll also want a few extras, which will make our users' lives easier:
-- A deadline for starting jobs (if we miss this deadline, we'll just wait till
- the next scheduled time)
-- What to do if multiple jobs would run at once (do we wait? stop the old one? run both?)
-- A way to pause the running of a CronJob, in case something's wrong with it
-- Limits on old job history
+ - A deadline for starting jobs (if we miss this deadline, we'll just wait till
+ the next scheduled time)
+ - What to do if multiple jobs would run at once (do we wait? stop the old one? run both?)
+ - A way to pause the running of a CronJob, in case something's wrong with it
+ - Limits on old job history
-Remember, since we never read our own status, we need to have some other way to
-keep track of whether a job has run. We can use at least one old job to do
-this.
+ Remember, since we never read our own status, we need to have some other way to
+ keep track of whether a job has run. We can use at least one old job to do
+ this.
-We'll use several markers (`// +comment`) to specify additional metadata. These
-will be used by [controller-tools](https://github.com/kubernetes-sigs/controller-tools) when generating our CRD manifest.
-As we'll see in a bit, controller-tools will also use GoDoc to form descriptions for
-the fields.
+ We'll use several markers (`// +comment`) to specify additional metadata. These
+ will be used by [controller-tools](https://github.com/kubernetes-sigs/controller-tools) when generating our CRD manifest.
+ As we'll see in a bit, controller-tools will also use GoDoc to form descriptions for
+ the fields.
*/
// CronJobSpec defines the desired state of CronJob
type CronJobSpec struct {
- //+kubebuilder:validation:MinLength=0
-
- // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
+ // schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
+ // +kubebuilder:validation:MinLength=0
+ // +required
Schedule string `json:"schedule"`
- //+kubebuilder:validation:Minimum=0
-
- // Optional deadline in seconds for starting the job if it misses scheduled
+ // startingDeadlineSeconds defines in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones.
// +optional
+ // +kubebuilder:validation:Minimum=0
StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
- // Specifies how to treat concurrent executions of a Job.
+ // concurrencyPolicy specifies how to treat concurrent executions of a Job.
// Valid values are:
// - "Allow" (default): allows CronJobs to run concurrently;
// - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet;
// - "Replace": cancels currently running job and replaces it with a new one
// +optional
+ // +kubebuilder:default:=Allow
ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
- // This flag tells the controller to suspend subsequent executions, it does
+ // suspend tells the controller to suspend subsequent executions, it does
// not apply to already started executions. Defaults to false.
// +optional
Suspend *bool `json:"suspend,omitempty"`
- // Specifies the job that will be created when executing a CronJob.
- JobTemplate batchv1beta1.JobTemplateSpec `json:"jobTemplate"`
+ // jobTemplate defines the job that will be created when executing a CronJob.
+ // +required
+ JobTemplate batchv1.JobTemplateSpec `json:"jobTemplate"`
- //+kubebuilder:validation:Minimum=0
-
- // The number of successful finished jobs to retain.
+ // successfulJobsHistoryLimit defines the number of successful finished jobs to retain.
// This is a pointer to distinguish between explicit zero and not specified.
// +optional
+ // +kubebuilder:validation:Minimum=0
SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
- //+kubebuilder:validation:Minimum=0
-
- // The number of failed finished jobs to retain.
+ // failedJobsHistoryLimit defines the number of failed finished jobs to retain.
// This is a pointer to distinguish between explicit zero and not specified.
// +optional
+ // +kubebuilder:validation:Minimum=0
FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"`
}
/*
-We define a custom type to hold our concurrency policy. It's actually
-just a string under the hood, but the type gives extra documentation,
-and allows us to attach validation on the type instead of the field,
-making the validation more easily reusable.
+ We define a custom type to hold our concurrency policy. It's actually
+ just a string under the hood, but the type gives extra documentation,
+ and allows us to attach validation on the type instead of the field,
+ making the validation more easily reusable.
*/
// ConcurrencyPolicy describes how the job will be handled.
@@ -132,54 +133,82 @@ const (
)
/*
-Next, let's design our status, which holds observed state. It contains any information
-we want users or other controllers to be able to easily obtain.
+ Next, let's design our status, which holds observed state. It contains any information
+ we want users or other controllers to be able to easily obtain.
-We'll keep a list of actively running jobs, as well as the last time that we successfully
-ran our job. Notice that we use `metav1.Time` instead of `time.Time` to get the stable
-serialization, as mentioned above.
+ We'll keep a list of actively running jobs, as well as the last time that we successfully
+ ran our job. Notice that we use `metav1.Time` instead of `time.Time` to get the stable
+ serialization, as mentioned above.
*/
-// CronJobStatus defines the observed state of CronJob
+// CronJobStatus defines the observed state of CronJob.
type CronJobStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
- // A list of pointers to currently running jobs.
+ // active defines a list of pointers to currently running jobs.
// +optional
+ // +listType=atomic
+ // +kubebuilder:validation:MinItems=1
+ // +kubebuilder:validation:MaxItems=10
Active []corev1.ObjectReference `json:"active,omitempty"`
- // Information when was the last time the job was successfully scheduled.
+ // lastScheduleTime defines when was the last time the job was successfully scheduled.
// +optional
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
+
+ // For Kubernetes API conventions, see:
+ // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
+
+ // conditions represent the current state of the CronJob resource.
+ // Each condition has a unique type and reflects the status of a specific aspect of the resource.
+ //
+ // Standard condition types include:
+ // - "Available": the resource is fully functional
+ // - "Progressing": the resource is being created or updated
+ // - "Degraded": the resource failed to reach or maintain its desired state
+ //
+ // The status of each condition is one of True, False, or Unknown.
+ // +listType=map
+ // +listMapKey=type
+ // +optional
+ Conditions []metav1.Condition `json:"conditions,omitempty"`
}
/*
-Finally, we have the rest of the boilerplate that we've already discussed.
-As previously noted, we don't need to change this, except to mark that
-we want a status subresource, so that we behave like built-in kubernetes types.
+ Finally, we have the rest of the boilerplate that we've already discussed.
+ As previously noted, we don't need to change this, except to mark that
+ we want a status subresource, so that we behave like built-in kubernetes types.
*/
-//+kubebuilder:object:root=true
-//+kubebuilder:subresource:status
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
// CronJob is the Schema for the cronjobs API
type CronJob struct {
/*
*/
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
+ metav1.TypeMeta `json:",inline"`
+
+ // metadata is a standard object metadata
+ // +optional
+ metav1.ObjectMeta `json:"metadata,omitzero"`
+
+ // spec defines the desired state of CronJob
+ // +required
+ Spec CronJobSpec `json:"spec"`
- Spec CronJobSpec `json:"spec,omitempty"`
- Status CronJobStatus `json:"status,omitempty"`
+ // status defines the observed state of CronJob
+ // +optional
+ Status CronJobStatus `json:"status,omitzero"`
}
-//+kubebuilder:object:root=true
+// +kubebuilder:object:root=true
// CronJobList contains a list of CronJob
type CronJobList struct {
metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata,omitempty"`
+ metav1.ListMeta `json:"metadata,omitzero"`
Items []CronJob `json:"items"`
}
@@ -187,4 +216,4 @@ func init() {
SchemeBuilder.Register(&CronJob{}, &CronJobList{})
}
-//+kubebuilder:docs-gen:collapse=Root Object Definitions
+// +kubebuilder:docs-gen:collapse=Root Object Definitions
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_webhook.go b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_webhook.go
deleted file mode 100644
index 716356560cf..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/cronjob_webhook.go
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-package v1
-
-import (
- "github.com/robfig/cron"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- validationutils "k8s.io/apimachinery/pkg/util/validation"
- "k8s.io/apimachinery/pkg/util/validation/field"
- ctrl "sigs.k8s.io/controller-runtime"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
-)
-
-// +kubebuilder:docs-gen:collapse=Go imports
-
-/*
-Next, we'll setup a logger for the webhooks.
-*/
-
-var cronjoblog = logf.Log.WithName("cronjob-resource")
-
-/*
-Then, we set up the webhook with the manager.
-*/
-
-func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(r).
- Complete()
-}
-
-/*
-Notice that we use kubebuilder markers to generate webhook manifests.
-This marker is responsible for generating a mutating webhook manifest.
-
-The meaning of each marker can be found [here](/reference/markers/webhook.md).
-*/
-
-//+kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
-
-/*
-We use the `webhook.Defaulter` interface to set defaults to our CRD.
-A webhook will automatically be served that calls this defaulting.
-
-The `Default` method is expected to mutate the receiver, setting the defaults.
-*/
-
-var _ webhook.Defaulter = &CronJob{}
-
-// Default implements webhook.Defaulter so a webhook will be registered for the type
-func (r *CronJob) Default() {
- cronjoblog.Info("default", "name", r.Name)
-
- if r.Spec.ConcurrencyPolicy == "" {
- r.Spec.ConcurrencyPolicy = AllowConcurrent
- }
- if r.Spec.Suspend == nil {
- r.Spec.Suspend = new(bool)
- }
- if r.Spec.SuccessfulJobsHistoryLimit == nil {
- r.Spec.SuccessfulJobsHistoryLimit = new(int32)
- *r.Spec.SuccessfulJobsHistoryLimit = 3
- }
- if r.Spec.FailedJobsHistoryLimit == nil {
- r.Spec.FailedJobsHistoryLimit = new(int32)
- *r.Spec.FailedJobsHistoryLimit = 1
- }
-}
-
-/*
-This marker is responsible for generating a validating webhook manifest.
-*/
-
-//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,versions=v1,name=vcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
-
-/*
-To validate our CRD beyond what's possible with declarative validation.
-Generally, declarative validation should be sufficient, but sometimes more
-advanced use cases call for complex validation.
-
-For instance, we'll see below that we use this to validate a well-formed cron
-schedule without making up a long regular expression.
-
-If `webhook.Validator` interface is implemented, a webhook will automatically be
-served that calls the validation.
-
-The `ValidateCreate`, `ValidateUpdate` and `ValidateDelete` methods are expected
-to validate that its receiver upon creation, update and deletion respectively.
-We separate out ValidateCreate from ValidateUpdate to allow behavior like making
-certain fields immutable, so that they can only be set on creation.
-ValidateDelete is also separated from ValidateUpdate to allow different
-validation behavior on deletion.
-Here, however, we just use the same shared validation for `ValidateCreate` and
-`ValidateUpdate`. And we do nothing in `ValidateDelete`, since we don't need to
-validate anything on deletion.
-*/
-
-var _ webhook.Validator = &CronJob{}
-
-// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateCreate() error {
- cronjoblog.Info("validate create", "name", r.Name)
-
- return r.validateCronJob()
-}
-
-// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateUpdate(old runtime.Object) error {
- cronjoblog.Info("validate update", "name", r.Name)
-
- return r.validateCronJob()
-}
-
-// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateDelete() error {
- cronjoblog.Info("validate delete", "name", r.Name)
-
- // TODO(user): fill in your validation logic upon object deletion.
- return nil
-}
-
-/*
-We validate the name and the spec of the CronJob.
-*/
-
-func (r *CronJob) validateCronJob() error {
- var allErrs field.ErrorList
- if err := r.validateCronJobName(); err != nil {
- allErrs = append(allErrs, err)
- }
- if err := r.validateCronJobSpec(); err != nil {
- allErrs = append(allErrs, err)
- }
- if len(allErrs) == 0 {
- return nil
- }
-
- return apierrors.NewInvalid(
- schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"},
- r.Name, allErrs)
-}
-
-/*
-Some fields are declaratively validated by OpenAPI schema.
-You can find kubebuilder validation markers (prefixed
-with `// +kubebuilder:validation`) in the
-[Designing an API](api-design.md) section.
-You can find all of the kubebuilder supported markers for
-declaring validation by running `controller-gen crd -w`,
-or [here](/reference/markers/crd-validation.md).
-*/
-
-func (r *CronJob) validateCronJobSpec() *field.Error {
- // The field helpers from the kubernetes API machinery help us return nicely
- // structured validation errors.
- return validateScheduleFormat(
- r.Spec.Schedule,
- field.NewPath("spec").Child("schedule"))
-}
-
-/*
-We'll need to validate the [cron](https://en.wikipedia.org/wiki/Cron) schedule
-is well-formatted.
-*/
-
-func validateScheduleFormat(schedule string, fldPath *field.Path) *field.Error {
- if _, err := cron.ParseStandard(schedule); err != nil {
- return field.Invalid(fldPath, schedule, err.Error())
- }
- return nil
-}
-
-/*
-Validating the length of a string field can be done declaratively by
-the validation schema.
-
-But the `ObjectMeta.Name` field is defined in a shared package under
-the apimachinery repo, so we can't declaratively validate it using
-the validation schema.
-*/
-
-func (r *CronJob) validateCronJobName() *field.Error {
- if len(r.ObjectMeta.Name) > validationutils.DNS1035LabelMaxLength-11 {
- // The job name length is 63 character like all Kubernetes objects
- // (which must fit in a DNS subdomain). The cronjob controller appends
- // a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
- // a job. The job name length limit is 63 characters. Therefore cronjob
- // names must have length <= 63-11=52. If we don't validate this here,
- // then job creation will fail later.
- return field.Invalid(field.NewPath("metadata").Child("name"), r.Name, "must be no more than 52 characters")
- }
- return nil
-}
-
-// +kubebuilder:docs-gen:collapse=Validate object name
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 57645bc1725..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 2021 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.
@@ -23,9 +23,9 @@ former, while the latter is used by the CRD generator to generate the right
metadata for the CRDs it creates from this package.
*/
-// Package v1 contains API Schema definitions for the batch v1 API group
-//+kubebuilder:object:generate=true
-//+groupName=batch.tutorial.kubebuilder.io
+// Package v1 contains API Schema definitions for the batch v1 API group.
+// +kubebuilder:object:generate=true
+// +groupName=batch.tutorial.kubebuilder.io
package v1
import (
@@ -39,11 +39,12 @@ Since we need to use all the types in this package in our controller, it's
helpful (and the convention) to have a convenient method to add all the types to
some other `Scheme`. SchemeBuilder makes this easy for us.
*/
+
var (
- // GroupVersion is group version used to register these objects
+ // GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v1"}
- // SchemeBuilder is used to add go types to the GroupVersionKind scheme
+ // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/webhook_suite_test.go b/docs/book/src/cronjob-tutorial/testdata/project/api/v1/webhook_suite_test.go
deleted file mode 100644
index 93992eb037c..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/api/v1/webhook_suite_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v1
-
-import (
- "context"
- "crypto/tls"
- "fmt"
- "net"
- "path/filepath"
- "testing"
- "time"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
-
- admissionv1beta1 "k8s.io/api/admission/v1beta1"
- //+kubebuilder:scaffold:imports
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/rest"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-)
-
-// 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
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Webhook Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- ctx, cancel = context.WithCancel(context.TODO())
-
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
- ErrorIfCRDPathMissing: false,
- WebhookInstallOptions: envtest.WebhookInstallOptions{
- Paths: []string{filepath.Join("..", "..", "config", "webhook")},
- },
- }
-
- cfg, err := testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
-
- scheme := runtime.NewScheme()
- err = AddToScheme(scheme)
- Expect(err).NotTo(HaveOccurred())
-
- err = admissionv1beta1.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())
-
- // start webhook server using Manager
- webhookInstallOptions := &testEnv.WebhookInstallOptions
- mgr, err := ctrl.NewManager(cfg, ctrl.Options{
- Scheme: scheme,
- Host: webhookInstallOptions.LocalServingHost,
- Port: webhookInstallOptions.LocalServingPort,
- CertDir: webhookInstallOptions.LocalServingCertDir,
- LeaderElection: false,
- MetricsBindAddress: "0",
- })
- Expect(err).NotTo(HaveOccurred())
-
- err = (&CronJob{}).SetupWebhookWithManager(mgr)
- Expect(err).NotTo(HaveOccurred())
-
- //+kubebuilder:scaffold:webhook
-
- go func() {
- err = mgr.Start(ctx)
- if err != nil {
- Expect(err).NotTo(HaveOccurred())
- }
- }()
-
- // wait for the webhook server to get ready
- dialer := &net.Dialer{Timeout: time.Second}
- addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
- Eventually(func() error {
- conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
- if err != nil {
- return err
- }
- conn.Close()
- return nil
- }).Should(Succeed())
-
-}, 60)
-
-var _ = AfterSuite(func() {
- cancel()
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
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 0e515fb28d2..19c26d31490 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 @@
-// +build !ignore_autogenerated
+//go:build !ignore_autogenerated
/*
-Copyright 2021 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.
@@ -15,7 +15,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-// +kubebuilder:gen-docs:collapse=Apache License
// Code generated by controller-gen. DO NOT EDIT.
@@ -23,7 +22,8 @@ package v1
import (
corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/runtime"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@@ -133,6 +133,13 @@ func (in *CronJobStatus) DeepCopyInto(out *CronJobStatus) {
in, out := &in.LastScheduleTime, &out.LastScheduleTime
*out = (*in).DeepCopy()
}
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]metav1.Condition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobStatus.
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go b/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go
new file mode 100644
index 00000000000..af7761518b0
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go
@@ -0,0 +1,244 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+package main
+
+import (
+ "crypto/tls"
+ "flag"
+ "os"
+
+ // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
+ // to ensure that exec-entrypoint and run can make use of them.
+ _ "k8s.io/client-go/plugin/pkg/client/auth"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ 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/healthz"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/filters"
+ metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+ "tutorial.kubebuilder.io/project/internal/controller"
+ webhookv1 "tutorial.kubebuilder.io/project/internal/webhook/v1"
+ // +kubebuilder:scaffold:imports
+)
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+/*
+The first difference to notice is that kubebuilder has added the new API
+group's package (`batchv1`) to our scheme. This means that we can use those
+objects in our controller.
+
+If we would be using any other CRD we would have to add their scheme the same way.
+Builtin types such as Job have their scheme added by `clientgoscheme`.
+*/
+
+var (
+ scheme = runtime.NewScheme()
+ setupLog = ctrl.Log.WithName("setup")
+)
+
+func init() {
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+
+ utilruntime.Must(batchv1.AddToScheme(scheme))
+ // +kubebuilder:scaffold:scheme
+}
+
+/*
+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
+ var enableHTTP2 bool
+ var tlsOpts []func(*tls.Config)
+ flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
+ flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
+ flag.BoolVar(&enableLeaderElection, "leader-elect", false,
+ "Enable leader election for controller manager. "+
+ "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{
+ Development: true,
+ }
+ opts.BindFlags(flag.CommandLine)
+ flag.Parse()
+
+ ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+
+ // if the enable-http2 flag is false (the default), http/2 should be disabled
+ // due to its vulnerabilities. More specifically, disabling http/2 will
+ // prevent from being vulnerable to the HTTP/2 Stream Cancellation and
+ // Rapid Reset CVEs. For more information see:
+ // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
+ // - https://github.com/advisories/GHSA-4374-p667-p6c8
+ disableHTTP2 := func(c *tls.Config) {
+ setupLog.Info("disabling http/2")
+ c.NextProtos = []string{"http/1.1"}
+ }
+
+ if !enableHTTP2 {
+ tlsOpts = append(tlsOpts, disableHTTP2)
+ }
+
+ // Initial webhook TLS options
+ webhookTLSOpts := tlsOpts
+ webhookServerOptions := webhook.Options{
+ TLSOpts: webhookTLSOpts,
+ }
+
+ 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)
+
+ webhookServerOptions.CertDir = webhookCertPath
+ webhookServerOptions.CertName = webhookCertName
+ webhookServerOptions.KeyName = webhookCertKey
+ }
+
+ webhookServer := webhook.NewServer(webhookServerOptions)
+
+ // 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.22.4/pkg/metrics/server
+ // - https://book.kubebuilder.io/reference/metrics.html
+ metricsServerOptions := metricsserver.Options{
+ BindAddress: metricsAddr,
+ SecureServing: secureMetrics,
+ TLSOpts: tlsOpts,
+ }
+
+ if secureMetrics {
+ // 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.22.4/pkg/metrics/filters#WithAuthenticationAndAuthorization
+ metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
+ }
+
+ // 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)
+
+ metricsServerOptions.CertDir = metricsCertPath
+ metricsServerOptions.CertName = metricsCertName
+ metricsServerOptions.KeyName = metricsCertKey
+ }
+
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ Metrics: metricsServerOptions,
+ WebhookServer: webhookServer,
+ HealthProbeBindAddress: probeAddr,
+ LeaderElection: enableLeaderElection,
+ LeaderElectionID: "80807133.tutorial.kubebuilder.io",
+ // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
+ // when the Manager ends. This requires the binary to immediately end when the
+ // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
+ // speeds up voluntary leader transitions as the new leader don't have to wait
+ // LeaseDuration time first.
+ //
+ // In the default scaffold provided, the program ends immediately after
+ // the manager stops, so would be fine to enable this option. However,
+ // if you are doing or is intended to do any operation such as perform cleanups
+ // after the manager stops then its usage might be unsafe.
+ // LeaderElectionReleaseOnCancel: true,
+ })
+ if err != nil {
+ setupLog.Error(err, "unable to start manager")
+ os.Exit(1)
+ }
+
+ // +kubebuilder:docs-gen:collapse=Remaining code from main.go
+
+ if err := (&controller.CronJobReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "CronJob")
+ os.Exit(1)
+ }
+
+ /*
+ We'll also set up webhooks for our type, which we'll talk about next.
+ We just need to add them to the manager. Since we might want to run
+ the webhooks separately, or not run them when testing our controller
+ locally, we'll put them behind an environment variable.
+
+ We'll just make sure to set `ENABLE_WEBHOOKS=false` when we run locally.
+ */
+ // nolint:goconst
+ if os.Getenv("ENABLE_WEBHOOKS") != "false" {
+ if err := webhookv1.SetupCronJobWebhookWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
+ os.Exit(1)
+ }
+ }
+ // +kubebuilder:scaffold:builder
+
+ if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up health check")
+ os.Exit(1)
+ }
+ if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up ready check")
+ os.Exit(1)
+ }
+
+ setupLog.Info("starting manager")
+ if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
+ setupLog.Error(err, "problem running manager")
+ os.Exit(1)
+ }
+}
+
+// +kubebuilder:docs-gen:collapse=Remaining code from main.go
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-webhook.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml
new file mode 100644
index 00000000000..ae025c9c6ed
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml
@@ -0,0 +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
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ labels:
+ 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
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.yaml
deleted file mode 100644
index 52d866183c7..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/certificate.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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:
- name: selfsigned-issuer
- namespace: system
-spec:
- selfSigned: {}
----
-apiVersion: cert-manager.io/v1
-kind: Certificate
-metadata:
- 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
- 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
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/certmanager/kustomizeconfig.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
index 90d7c313ca1..cf6f89e8892 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
@@ -1,4 +1,4 @@
-# This configuration is for teaching kustomize how to update name ref and var substitution
+# This configuration is for teaching kustomize how to update name ref substitution
nameReference:
- kind: Issuer
group: cert-manager.io
@@ -6,11 +6,3 @@ nameReference:
- kind: Certificate
group: cert-manager.io
path: spec/issuerRef/name
-
-varReference:
-- kind: Certificate
- group: cert-manager.io
- path: spec/commonName
-- kind: Certificate
- group: cert-manager.io
- path: spec/dnsNames
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 dce10cb4612..6797dcc156e 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
@@ -1,11 +1,9 @@
-
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.4.1
- creationTimestamp: null
+ controller-gen.kubebuilder.io/version: v0.19.0
name: cronjobs.batch.tutorial.kubebuilder.io
spec:
group: batch.tutorial.kubebuilder.io
@@ -19,287 +17,203 @@ spec:
- name: v1
schema:
openAPIV3Schema:
- description: CronJob is the Schema for the cronjobs API
properties:
apiVersion:
- description: 'APIVersion defines the versioned schema of this representation
- of an object. Servers should convert recognized schemas to the latest
- internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
- description: 'Kind is a string value representing the REST resource this
- object represents. Servers may infer this from the endpoint the client
- submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
- description: CronJobSpec defines the desired state of CronJob
properties:
concurrencyPolicy:
- description: 'Specifies how to treat concurrent executions of a Job.
- Valid values are: - "Allow" (default): allows CronJobs to run concurrently;
- - "Forbid": forbids concurrent runs, skipping next run if previous
- run hasn''t finished yet; - "Replace": cancels currently running
- job and replaces it with a new one'
+ default: Allow
enum:
- Allow
- Forbid
- Replace
type: string
failedJobsHistoryLimit:
- description: The number of failed finished jobs to retain. This is
- a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
jobTemplate:
- description: Specifies the job that will be created when executing
- a CronJob.
properties:
metadata:
- description: 'Standard object''s metadata of the jobs created
- from this template. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
type: object
spec:
- description: 'Specification of the desired behavior of the job.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
properties:
activeDeadlineSeconds:
- description: Specifies the duration in seconds relative to
- the startTime that the job may be active before the system
- tries to terminate it; value must be positive integer
format: int64
type: integer
backoffLimit:
- description: Specifies the number of retries before marking
- this job failed. Defaults to 6
format: int32
type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
completions:
- description: 'Specifies the desired number of successfully
- finished pods the job should be run with. Setting to nil
- means that the success of any pod signals the success of
- all pods, and allows parallelism to have any positive value. Setting
- to 1 means that parallelism is limited to 1 and the success
- of that pod signals the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
format: int32
type: integer
+ managedBy:
+ type: string
manualSelector:
- description: 'manualSelector controls generation of pod labels
- and pod selectors. Leave `manualSelector` unset unless you
- are certain what you are doing. When false or unset, the
- system pick labels unique to this job and appends those
- labels to the pod template. When true, the user is responsible
- for picking unique labels and specifying the selector. Failure
- to pick a unique label may cause this and other jobs to
- not function correctly. However, You may see `manualSelector=true`
- in jobs that were created with the old `extensions/v1beta1`
- API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector'
type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
parallelism:
- description: 'Specifies the maximum desired number of pods
- the job should run at any given time. The actual number
- of pods running in steady state will be less than this number
- when ((.spec.completions - .status.successful) < .spec.parallelism),
- i.e. when the work left to do is less than max parallelism.
- More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
format: int32
type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
selector:
- description: 'A label query over pods that should match the
- pod count. Normally, the system sets this field for you.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors'
properties:
matchExpressions:
- description: matchExpressions is a list of label selector
- requirements. The requirements are ANDed.
items:
- description: A label selector requirement is a selector
- that contains values, a key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the label key that the selector
- applies to.
type: string
operator:
- description: operator represents a key's relationship
- to a set of values. Valid operators are In, NotIn,
- Exists and DoesNotExist.
type: string
values:
- description: values is an array of string values.
- If the operator is In or NotIn, the values array
- must be non-empty. If the operator is Exists or
- DoesNotExist, the values array must be empty.
- This array is replaced during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map of {key,value} pairs.
- A single {key,value} in the matchLabels map is equivalent
- to an element of matchExpressions, whose key field is
- "key", the operator is "In", and the values array contains
- only "value". The requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
template:
- description: 'Describes the pod that will be created when
- executing a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
properties:
metadata:
- description: 'Standard object''s metadata. More info:
- https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
type: object
spec:
- description: 'Specification of the desired behavior of
- the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
properties:
activeDeadlineSeconds:
- description: Optional duration in seconds the pod
- may be active on the node relative to StartTime
- before the system will actively try to mark it failed
- and kill associated containers. Value must be a
- positive integer.
format: int64
type: integer
affinity:
- description: If specified, the pod's scheduling constraints
properties:
nodeAffinity:
- description: Describes node affinity scheduling
- rules for the pod.
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- affinity expressions specified by this field,
- but it may choose a node that violates one
- or more of the expressions. The node that
- is most preferred is the one with the greatest
- sum of weights, i.e. for each node that
- meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- affinity expressions, etc.), compute a sum
- by iterating through the elements of this
- field and adding "weight" to the sum if
- the node matches the corresponding matchExpressions;
- the node(s) with the highest sum are the
- most preferred.
items:
- description: An empty preferred scheduling
- term matches all objects with implicit
- weight 0 (i.e. it's a no-op). A null preferred
- scheduling term matches no objects (i.e.
- is also a no-op).
properties:
preference:
- description: A node selector term, associated
- with the corresponding weight.
properties:
matchExpressions:
- description: A list of node selector
- requirements by node's labels.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchFields:
- description: A list of node selector
- requirements by node's fields.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
+ x-kubernetes-map-type: atomic
weight:
- description: Weight associated with
- matching the corresponding nodeSelectorTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -307,244 +221,137 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the affinity requirements specified
- by this field cease to be met at some point
- during pod execution (e.g. due to an update),
- the system may or may not try to eventually
- evict the pod from its node.
properties:
nodeSelectorTerms:
- description: Required. A list of node
- selector terms. The terms are ORed.
items:
- description: A null or empty node selector
- term matches no objects. The requirements
- of them are ANDed. The TopologySelectorTerm
- type implements a subset of the NodeSelectorTerm.
properties:
matchExpressions:
- description: A list of node selector
- requirements by node's labels.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchFields:
- description: A list of node selector
- requirements by node's fields.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
+ x-kubernetes-map-type: atomic
type: array
+ x-kubernetes-list-type: atomic
required:
- nodeSelectorTerms
type: object
+ x-kubernetes-map-type: atomic
type: object
podAffinity:
- description: Describes pod affinity scheduling
- rules (e.g. co-locate this pod in the same node,
- zone, etc. as some other pod(s)).
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- affinity expressions specified by this field,
- but it may choose a node that violates one
- or more of the expressions. The node that
- is most preferred is the one with the greatest
- sum of weights, i.e. for each node that
- meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- affinity expressions, etc.), compute a sum
- by iterating through the elements of this
- field and adding "weight" to the sum if
- the node has pods which matches the corresponding
- podAffinityTerm; the node(s) with the highest
- sum are the most preferred.
items:
- description: The weights of all of the matched
- WeightedPodAffinityTerm fields are added
- per-node to find the most preferred node(s)
properties:
podAffinityTerm:
- description: Required. A pod affinity
- term, associated with the corresponding
- weight.
properties:
labelSelector:
- description: A label query over
- a set of resources, in this case
- pods.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies
- which namespaces the labelSelector
- applies to (matches against);
- null or empty list means "this
- pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be
- co-located (affinity) or not co-located
- (anti-affinity) with the pods
- matching the labelSelector in
- the specified namespaces, where
- co-located is defined as running
- on a node whose value of the label
- with key topologyKey matches that
- of any node on which any of the
- selected pods is running. Empty
- topologyKey is not allowed.
type: string
required:
- topologyKey
type: object
weight:
- description: weight associated with
- matching the corresponding podAffinityTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -552,240 +359,165 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the affinity requirements specified
- by this field cease to be met at some point
- during pod execution (e.g. due to a pod
- label update), the system may or may not
- try to eventually evict the pod from its
- node. When there are multiple elements,
- the lists of nodes corresponding to each
- podAffinityTerm are intersected, i.e. all
- terms must be satisfied.
items:
- description: Defines a set of pods (namely
- those matching the labelSelector relative
- to the given namespace(s)) that this pod
- should be co-located (affinity) or not
- co-located (anti-affinity) with, where
- co-located is defined as running on a
- node whose value of the label with key
- matches that of any node
- on which a pod of the set of pods is running
properties:
labelSelector:
- description: A label query over a set
- of resources, in this case pods.
properties:
matchExpressions:
- description: matchExpressions is
- a list of label selector requirements.
- The requirements are ANDed.
items:
- description: A label selector
- requirement is a selector that
- contains values, a key, and
- an operator that relates the
- key and values.
properties:
key:
- description: key is the label
- key that the selector applies
- to.
type: string
operator:
- description: operator represents
- a key's relationship to
- a set of values. Valid operators
- are In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is an
- array of string values.
- If the operator is In or
- NotIn, the values array
- must be non-empty. If the
- operator is Exists or DoesNotExist,
- the values array must be
- empty. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map
- of {key,value} pairs. A single
- {key,value} in the matchLabels
- map is equivalent to an element
- of matchExpressions, whose key
- field is "key", the operator is
- "In", and the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies which
- namespaces the labelSelector applies
- to (matches against); null or empty
- list means "this pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be co-located
- (affinity) or not co-located (anti-affinity)
- with the pods matching the labelSelector
- in the specified namespaces, where
- co-located is defined as running on
- a node whose value of the label with
- key topologyKey matches that of any
- node on which any of the selected
- pods is running. Empty topologyKey
- is not allowed.
type: string
required:
- topologyKey
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
podAntiAffinity:
- description: Describes pod anti-affinity scheduling
- rules (e.g. avoid putting this pod in the same
- node, zone, etc. as some other pod(s)).
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- anti-affinity expressions specified by this
- field, but it may choose a node that violates
- one or more of the expressions. The node
- that is most preferred is the one with the
- greatest sum of weights, i.e. for each node
- that meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- anti-affinity expressions, etc.), compute
- a sum by iterating through the elements
- of this field and adding "weight" to the
- sum if the node has pods which matches the
- corresponding podAffinityTerm; the node(s)
- with the highest sum are the most preferred.
items:
- description: The weights of all of the matched
- WeightedPodAffinityTerm fields are added
- per-node to find the most preferred node(s)
properties:
podAffinityTerm:
- description: Required. A pod affinity
- term, associated with the corresponding
- weight.
properties:
labelSelector:
- description: A label query over
- a set of resources, in this case
- pods.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies
- which namespaces the labelSelector
- applies to (matches against);
- null or empty list means "this
- pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be
- co-located (affinity) or not co-located
- (anti-affinity) with the pods
- matching the labelSelector in
- the specified namespaces, where
- co-located is defined as running
- on a node whose value of the label
- with key topologyKey matches that
- of any node on which any of the
- selected pods is running. Empty
- topologyKey is not allowed.
type: string
required:
- topologyKey
type: object
weight:
- description: weight associated with
- matching the corresponding podAffinityTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -793,764 +525,440 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the anti-affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the anti-affinity requirements
- specified by this field cease to be met
- at some point during pod execution (e.g.
- due to a pod label update), the system may
- or may not try to eventually evict the pod
- from its node. When there are multiple elements,
- the lists of nodes corresponding to each
- podAffinityTerm are intersected, i.e. all
- terms must be satisfied.
items:
- description: Defines a set of pods (namely
- those matching the labelSelector relative
- to the given namespace(s)) that this pod
- should be co-located (affinity) or not
- co-located (anti-affinity) with, where
- co-located is defined as running on a
- node whose value of the label with key
- matches that of any node
- on which a pod of the set of pods is running
properties:
labelSelector:
- description: A label query over a set
- of resources, in this case pods.
properties:
matchExpressions:
- description: matchExpressions is
- a list of label selector requirements.
- The requirements are ANDed.
items:
- description: A label selector
- requirement is a selector that
- contains values, a key, and
- an operator that relates the
- key and values.
properties:
key:
- description: key is the label
- key that the selector applies
- to.
type: string
operator:
- description: operator represents
- a key's relationship to
- a set of values. Valid operators
- are In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is an
- array of string values.
- If the operator is In or
- NotIn, the values array
- must be non-empty. If the
- operator is Exists or DoesNotExist,
- the values array must be
- empty. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map
- of {key,value} pairs. A single
- {key,value} in the matchLabels
- map is equivalent to an element
- of matchExpressions, whose key
- field is "key", the operator is
- "In", and the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies which
- namespaces the labelSelector applies
- to (matches against); null or empty
- list means "this pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be co-located
- (affinity) or not co-located (anti-affinity)
- with the pods matching the labelSelector
- in the specified namespaces, where
- co-located is defined as running on
- a node whose value of the label with
- key topologyKey matches that of any
- node on which any of the selected
- pods is running. Empty topologyKey
- is not allowed.
type: string
required:
- topologyKey
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
type: object
automountServiceAccountToken:
- description: AutomountServiceAccountToken indicates
- whether a service account token should be automatically
- mounted.
type: boolean
containers:
- description: List of containers belonging to the pod.
- Containers cannot currently be added or removed.
- There must be at least one container in a Pod. Cannot
- be updated.
items:
- description: A single application container that
- you want to run within a pod.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images
- This field is optional to allow higher level
- config management to default or override container
- images in workload controllers like Deployments
- and StatefulSets.'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Actions that the management system
- should take in response to container lifecycle
- events. Cannot be updated.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: 'Periodic probe of container liveness.
- Container will be restarted if the probe fails.
- Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the container specified
- as a DNS_LABEL. Each container in a pod must
- have a unique name (DNS_LABEL). Cannot be
- updated.
type: string
ports:
- description: List of ports to expose from the
- container. Exposing a port here gives the
- system additional information about the network
- connections a container uses, but is primarily
- informational. Not specifying a port here
- DOES NOT prevent that port from being exposed.
- Any port which is listening on the default
- "0.0.0.0" address inside a container will
- be accessible from the network. Cannot be
- updated.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
@@ -1561,145 +969,115 @@ spec:
- protocol
x-kubernetes-list-type: map
readinessProbe:
- description: 'Periodic probe of container service
- readiness. Container will be removed from
- service endpoints if the probe fails. Cannot
- be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: 'Compute Resources required by
- this container. Cannot be updated. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -1707,9 +1085,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -1718,1307 +1093,743 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: 'Security options the pod should
- run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
- More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: 'StartupProbe indicates that the
- Pod has successfully initialized. If specified,
- no other probes are executed until this completes
- successfully. If this probe fails, the Pod
- will be restarted, just as if the livenessProbe
- failed. This can be used to provide different
- probe parameters at the beginning of a Pod''s
- lifecycle, when it might take a long time
- to load data or warm a cache, than during
- steady-state operation. This cannot be updated.
- This is a beta feature enabled by the StartupProbe
- feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
dnsConfig:
- description: Specifies the DNS parameters of a pod.
- Parameters specified here will be merged to the
- generated DNS configuration based on DNSPolicy.
properties:
nameservers:
- description: A list of DNS name server IP addresses.
- This will be appended to the base nameservers
- generated from DNSPolicy. Duplicated nameservers
- will be removed.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
options:
- description: A list of DNS resolver options. This
- will be merged with the base options generated
- from DNSPolicy. Duplicated entries will be removed.
- Resolution options given in Options will override
- those that appear in the base DNSPolicy.
items:
- description: PodDNSConfigOption defines DNS
- resolver options of a pod.
properties:
name:
- description: Required.
type: string
value:
type: string
type: object
type: array
+ x-kubernetes-list-type: atomic
searches:
- description: A list of DNS search domains for
- host-name lookup. This will be appended to the
- base search paths generated from DNSPolicy.
- Duplicated search paths will be removed.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
dnsPolicy:
- description: Set DNS policy for the pod. Defaults
- to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet',
- 'ClusterFirst', 'Default' or 'None'. DNS parameters
- given in DNSConfig will be merged with the policy
- selected with DNSPolicy. To have DNS options set
- along with hostNetwork, you have to specify DNS
- policy explicitly to 'ClusterFirstWithHostNet'.
type: string
enableServiceLinks:
- description: 'EnableServiceLinks indicates whether
- information about services should be injected into
- pod''s environment variables, matching the syntax
- of Docker links. Optional: Defaults to true.'
type: boolean
ephemeralContainers:
- description: List of ephemeral containers run in this
- pod. Ephemeral containers may be run in an existing
- pod to perform user-initiated actions such as debugging.
- This list cannot be specified when creating a pod,
- and it cannot be modified by updating the pod spec.
- In order to add an ephemeral container to an existing
- pod, use the pod's ephemeralcontainers subresource.
- This field is alpha-level and is only honored by
- servers that enable the EphemeralContainers feature.
items:
- description: An EphemeralContainer is a container
- that may be added temporarily to an existing pod
- for user-initiated activities such as debugging.
- Ephemeral containers have no resource or scheduling
- guarantees, and they will not be restarted when
- they exit or when a pod is removed or restarted.
- If an ephemeral container causes a pod to exceed
- its resource allocation, the pod may be evicted.
- Ephemeral containers may not be added by directly
- updating the pod spec. They must be added via
- the pod's ephemeralcontainers subresource, and
- they will appear in the pod spec once added. This
- is an alpha feature enabled by the EphemeralContainers
- feature flag.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Lifecycle is not allowed for ephemeral
- containers.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the ephemeral container
- specified as a DNS_LABEL. This name must be
- unique among all containers, init containers
- and ephemeral containers.
type: string
ports:
- description: Ports are not allowed for ephemeral
- containers.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
readinessProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: Resources are not allowed for ephemeral
- containers. Ephemeral containers use spare
- resources already allocated to the pod.
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -3026,9 +1837,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -3037,1178 +1845,640 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: SecurityContext is not allowed
- for ephemeral containers.
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
targetContainerName:
- description: If set, the name of the container
- from PodSpec that this ephemeral container
- targets. The ephemeral container will be run
- in the namespaces (IPC, PID, etc) of this
- container. If not set then the ephemeral container
- is run in whatever namespaces are shared for
- the pod. Note that the container runtime must
- support this feature.
type: string
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
hostAliases:
- description: HostAliases is an optional list of hosts
- and IPs that will be injected into the pod's hosts
- file if specified. This is only valid for non-hostNetwork
- pods.
items:
- description: HostAlias holds the mapping between
- IP and hostnames that will be injected as an entry
- in the pod's hosts file.
properties:
hostnames:
- description: Hostnames for the above IP address.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
ip:
- description: IP address of the host file entry.
type: string
+ required:
+ - ip
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
hostIPC:
- description: 'Use the host''s ipc namespace. Optional:
- Default to false.'
type: boolean
hostNetwork:
- description: Host networking requested for this pod.
- Use the host's network namespace. If this option
- is set, the ports that will be used must be specified.
- Default to false.
type: boolean
hostPID:
- description: 'Use the host''s pid namespace. Optional:
- Default to false.'
+ type: boolean
+ hostUsers:
type: boolean
hostname:
- description: Specifies the hostname of the Pod If
- not specified, the pod's hostname will be set to
- a system-defined value.
+ type: string
+ hostnameOverride:
type: string
imagePullSecrets:
- description: 'ImagePullSecrets is an optional list
- of references to secrets in the same namespace to
- use for pulling any of the images used by this PodSpec.
- If specified, these secrets will be passed to individual
- puller implementations for them to use. For example,
- in the case of docker, only DockerConfig type secrets
- are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod'
items:
- description: LocalObjectReference contains enough
- information to let you locate the referenced object
- inside the same namespace.
properties:
name:
- description: 'Name of the referent. More info:
- https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
initContainers:
- description: 'List of initialization containers belonging
- to the pod. Init containers are executed in order
- prior to containers being started. If any init container
- fails, the pod is considered to have failed and
- is handled according to its restartPolicy. The name
- for an init container or normal container must be
- unique among all containers. Init containers may
- not have Lifecycle actions, Readiness probes, Liveness
- probes, or Startup probes. The resourceRequirements
- of an init container are taken into account during
- scheduling by finding the highest request/limit
- for each resource type, and then using the max of
- of that value or the sum of the normal containers.
- Limits are applied to init containers in a similar
- fashion. Init containers cannot currently be added
- or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/'
items:
- description: A single application container that
- you want to run within a pod.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images
- This field is optional to allow higher level
- config management to default or override container
- images in workload controllers like Deployments
- and StatefulSets.'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Actions that the management system
- should take in response to container lifecycle
- events. Cannot be updated.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: 'Periodic probe of container liveness.
- Container will be restarted if the probe fails.
- Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the container specified
- as a DNS_LABEL. Each container in a pod must
- have a unique name (DNS_LABEL). Cannot be
- updated.
type: string
ports:
- description: List of ports to expose from the
- container. Exposing a port here gives the
- system additional information about the network
- connections a container uses, but is primarily
- informational. Not specifying a port here
- DOES NOT prevent that port from being exposed.
- Any port which is listening on the default
- "0.0.0.0" address inside a container will
- be accessible from the network. Cannot be
- updated.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
@@ -4219,145 +2489,115 @@ spec:
- protocol
x-kubernetes-list-type: map
readinessProbe:
- description: 'Periodic probe of container service
- readiness. Container will be removed from
- service endpoints if the probe fails. Cannot
- be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: 'Compute Resources required by
- this container. Cannot be updated. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -4365,9 +2605,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -4376,477 +2613,258 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: 'Security options the pod should
- run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
- More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: 'StartupProbe indicates that the
- Pod has successfully initialized. If specified,
- no other probes are executed until this completes
- successfully. If this probe fails, the Pod
- will be restarted, just as if the livenessProbe
- failed. This can be used to provide different
- probe parameters at the beginning of a Pod''s
- lifecycle, when it might take a long time
- to load data or warm a cache, than during
- steady-state operation. This cannot be updated.
- This is a beta feature enabled by the StartupProbe
- feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
nodeName:
- description: NodeName is a request to schedule this
- pod onto a specific node. If it is non-empty, the
- scheduler simply schedules this pod onto that node,
- assuming that it fits resource requirements.
type: string
nodeSelector:
additionalProperties:
type: string
- description: 'NodeSelector is a selector which must
- be true for the pod to fit on a node. Selector which
- must match a node''s labels for the pod to be scheduled
- on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/'
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
type: object
overhead:
additionalProperties:
@@ -4855,472 +2873,247 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Overhead represents the resource overhead
- associated with running a pod for a given RuntimeClass.
- This field will be autopopulated at admission time
- by the RuntimeClass admission controller. If the
- RuntimeClass admission controller is enabled, overhead
- must not be set in Pod create requests. The RuntimeClass
- admission controller will reject Pod create requests
- which have the overhead already set. If RuntimeClass
- is configured and selected in the PodSpec, Overhead
- will be set to the value defined in the corresponding
- RuntimeClass, otherwise it will remain unset and
- treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md
- This field is alpha-level as of Kubernetes v1.16,
- and is only honored by servers that enable the PodOverhead
- feature.'
type: object
preemptionPolicy:
- description: PreemptionPolicy is the Policy for preempting
- pods with lower priority. One of Never, PreemptLowerPriority.
- Defaults to PreemptLowerPriority if unset. This
- field is beta-level, gated by the NonPreemptingPriority
- feature-gate.
type: string
priority:
- description: The priority value. Various system components
- use this field to find the priority of the pod.
- When Priority Admission Controller is enabled, it
- prevents users from setting this field. The admission
- controller populates this field from PriorityClassName.
- The higher the value, the higher the priority.
format: int32
type: integer
priorityClassName:
- description: If specified, indicates the pod's priority.
- "system-node-critical" and "system-cluster-critical"
- are two special keywords which indicate the highest
- priorities with the former being the highest priority.
- Any other name must be defined by creating a PriorityClass
- object with that name. If not specified, the pod
- priority will be default or zero if there is no
- default.
type: string
readinessGates:
- description: 'If specified, all readiness gates will
- be evaluated for pod readiness. A pod is ready when
- all its containers are ready AND all conditions
- specified in the readiness gates have status equal
- to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md'
items:
- description: PodReadinessGate contains the reference
- to a pod condition
properties:
conditionType:
- description: ConditionType refers to a condition
- in the pod's condition list with matching
- type.
type: string
required:
- conditionType
type: object
type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
restartPolicy:
- description: 'Restart policy for all containers within
- the pod. One of Always, OnFailure, Never. Default
- to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy'
type: string
runtimeClassName:
- description: 'RuntimeClassName refers to a RuntimeClass
- object in the node.k8s.io group, which should be
- used to run this pod. If no RuntimeClass resource
- matches the named class, the pod will not be run.
- If unset or empty, the "legacy" RuntimeClass will
- be used, which is an implicit class with an empty
- definition that uses the default runtime handler.
- More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md
- This is a beta feature as of Kubernetes v1.14.'
type: string
schedulerName:
- description: If specified, the pod will be dispatched
- by specified scheduler. If not specified, the pod
- will be dispatched by default scheduler.
type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
securityContext:
- description: 'SecurityContext holds pod-level security
- attributes and common container settings. Optional:
- Defaults to empty. See type description for default
- values of each field.'
properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
fsGroup:
- description: "A special supplemental group that
- applies to all containers in a pod. Some volume
- types allow the Kubelet to change the ownership
- of that volume to be owned by the pod: \n 1.
- The owning GID will be the FSGroup 2. The setgid
- bit is set (new files created in the volume
- will be owned by FSGroup) 3. The permission
- bits are OR'd with rw-rw---- \n If unset, the
- Kubelet will not modify the ownership and permissions
- of any volume."
format: int64
type: integer
fsGroupChangePolicy:
- description: 'fsGroupChangePolicy defines behavior
- of changing ownership and permission of the
- volume before being exposed inside Pod. This
- field will only apply to volume types which
- support fsGroup based ownership(and permissions).
- It will have no effect on ephemeral volume types
- such as: secret, configmaps and emptydir. Valid
- values are "OnRootMismatch" and "Always". If
- not specified defaults to "Always".'
type: string
runAsGroup:
- description: The GID to run the entrypoint of
- the container process. Uses runtime default
- if unset. May also be set in SecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence for that container.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container must
- run as a non-root user. If true, the Kubelet
- will validate the image at runtime to ensure
- that it does not run as UID 0 (root) and fail
- to start the container if it does. If unset
- or false, no such validation will be performed.
- May also be set in SecurityContext. If set
- in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint of
- the container process. Defaults to user specified
- in image metadata if unspecified. May also be
- set in SecurityContext. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence for that
- container.
format: int64
type: integer
+ seLinuxChangePolicy:
+ type: string
seLinuxOptions:
- description: The SELinux context to be applied
- to all containers. If unspecified, the container
- runtime will allocate a random SELinux context
- for each container. May also be set in SecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence for that container.
properties:
level:
- description: Level is SELinux level label
- that applies to the container.
type: string
role:
- description: Role is a SELinux role label
- that applies to the container.
type: string
type:
- description: Type is a SELinux type label
- that applies to the container.
type: string
user:
- description: User is a SELinux user label
- that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use by the
- containers in this pod.
properties:
localhostProfile:
- description: localhostProfile indicates a
- profile defined in a file on the node should
- be used. The profile must be preconfigured
- on the node to work. Must be a descending
- path, relative to the kubelet's configured
- seccomp profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind of
- seccomp profile will be applied. Valid options
- are: \n Localhost - a profile defined in
- a file on the node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
supplementalGroups:
- description: A list of groups applied to the first
- process run in each container, in addition to
- the container's primary GID. If unspecified,
- no groups will be added to any container.
items:
format: int64
type: integer
type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
sysctls:
- description: Sysctls hold a list of namespaced
- sysctls used for the pod. Pods with unsupported
- sysctls (by the container runtime) might fail
- to launch.
items:
- description: Sysctl defines a kernel parameter
- to be set
properties:
name:
- description: Name of a property to set
type: string
value:
- description: Value of a property to set
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
windowsOptions:
- description: The Windows specific settings applied
- to all containers. If unspecified, the options
- within a container's SecurityContext will be
- used. If set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where the
- GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName is the
- name of the GMSA credential spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows to run
- the entrypoint of the container process.
- Defaults to the user specified in image
- metadata if unspecified. May also be set
- in PodSecurityContext. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
serviceAccount:
- description: 'DeprecatedServiceAccount is a depreciated
- alias for ServiceAccountName. Deprecated: Use serviceAccountName
- instead.'
type: string
serviceAccountName:
- description: 'ServiceAccountName is the name of the
- ServiceAccount to use to run this pod. More info:
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'
type: string
setHostnameAsFQDN:
- description: If true the pod's hostname will be configured
- as the pod's FQDN, rather than the leaf name (the
- default). In Linux containers, this means setting
- the FQDN in the hostname field of the kernel (the
- nodename field of struct utsname). In Windows containers,
- this means setting the registry value of hostname
- for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters
- to FQDN. If a pod does not have FQDN, this has no
- effect. Default to false.
type: boolean
shareProcessNamespace:
- description: 'Share a single process namespace between
- all of the containers in a pod. When this is set
- containers will be able to view and signal processes
- from other containers in the same pod, and the first
- process in each container will not be assigned PID
- 1. HostPID and ShareProcessNamespace cannot both
- be set. Optional: Default to false.'
type: boolean
subdomain:
- description: If specified, the fully qualified Pod
- hostname will be "...svc.". If not specified, the pod will not have
- a domainname at all.
type: string
terminationGracePeriodSeconds:
- description: Optional duration in seconds the pod
- needs to terminate gracefully. May be decreased
- in delete request. Value must be non-negative integer.
- The value zero indicates delete immediately. If
- this value is nil, the default grace period will
- be used instead. The grace period is the duration
- in seconds after the processes running in the pod
- are sent a termination signal and the time when
- the processes are forcibly halted with a kill signal.
- Set this value longer than the expected cleanup
- time for your process. Defaults to 30 seconds.
format: int64
type: integer
tolerations:
- description: If specified, the pod's tolerations.
items:
- description: The pod this Toleration is attached
- to tolerates any taint that matches the triple
- using the matching operator
- .
properties:
effect:
- description: Effect indicates the taint effect
- to match. Empty means match all taint effects.
- When specified, allowed values are NoSchedule,
- PreferNoSchedule and NoExecute.
type: string
key:
- description: Key is the taint key that the toleration
- applies to. Empty means match all taint keys.
- If the key is empty, operator must be Exists;
- this combination means to match all values
- and all keys.
type: string
operator:
- description: Operator represents a key's relationship
- to the value. Valid operators are Exists and
- Equal. Defaults to Equal. Exists is equivalent
- to wildcard for value, so that a pod can tolerate
- all taints of a particular category.
type: string
tolerationSeconds:
- description: TolerationSeconds represents the
- period of time the toleration (which must
- be of effect NoExecute, otherwise this field
- is ignored) tolerates the taint. By default,
- it is not set, which means tolerate the taint
- forever (do not evict). Zero and negative
- values will be treated as 0 (evict immediately)
- by the system.
format: int64
type: integer
value:
- description: Value is the taint value the toleration
- matches to. If the operator is Exists, the
- value should be empty, otherwise just a regular
- string.
type: string
type: object
type: array
+ x-kubernetes-list-type: atomic
topologySpreadConstraints:
- description: TopologySpreadConstraints describes how
- a group of pods ought to spread across topology
- domains. Scheduler will schedule pods in a way which
- abides by the constraints. All topologySpreadConstraints
- are ANDed.
items:
- description: TopologySpreadConstraint specifies
- how to spread matching pods among the given topology.
properties:
labelSelector:
- description: LabelSelector is used to find matching
- pods. Pods that match this label selector
- are counted to determine the number of pods
- in their corresponding topology domain.
properties:
matchExpressions:
- description: matchExpressions is a list
- of label selector requirements. The requirements
- are ANDed.
items:
- description: A label selector requirement
- is a selector that contains values,
- a key, and an operator that relates
- the key and values.
properties:
key:
- description: key is the label key
- that the selector applies to.
type: string
operator:
- description: operator represents a
- key's relationship to a set of values.
- Valid operators are In, NotIn, Exists
- and DoesNotExist.
type: string
values:
- description: values is an array of
- string values. If the operator is
- In or NotIn, the values array must
- be non-empty. If the operator is
- Exists or DoesNotExist, the values
- array must be empty. This array
- is replaced during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map of {key,value}
- pairs. A single {key,value} in the matchLabels
- map is equivalent to an element of matchExpressions,
- whose key field is "key", the operator
- is "In", and the values array contains
- only "value". The requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
maxSkew:
- description: 'MaxSkew describes the degree to
- which pods may be unevenly distributed. When
- `whenUnsatisfiable=DoNotSchedule`, it is the
- maximum permitted difference between the number
- of matching pods in the target topology and
- the global minimum. For example, in a 3-zone
- cluster, MaxSkew is set to 1, and pods with
- the same labelSelector spread as 1/1/0: |
- zone1 | zone2 | zone3 | | P | P | |
- - if MaxSkew is 1, incoming pod can only be
- scheduled to zone3 to become 1/1/1; scheduling
- it onto zone1(zone2) would make the ActualSkew(2-0)
- on zone1(zone2) violate MaxSkew(1). - if MaxSkew
- is 2, incoming pod can be scheduled onto any
- zone. When `whenUnsatisfiable=ScheduleAnyway`,
- it is used to give higher precedence to topologies
- that satisfy it. It''s a required field. Default
- value is 1 and 0 is not allowed.'
format: int32
type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
topologyKey:
- description: TopologyKey is the key of node
- labels. Nodes that have a label with this
- key and identical values are considered to
- be in the same topology. We consider each
- as a "bucket", and try to put
- balanced number of pods into each bucket.
- It's a required field.
type: string
whenUnsatisfiable:
- description: 'WhenUnsatisfiable indicates how
- to deal with a pod if it doesn''t satisfy
- the spread constraint. - DoNotSchedule (default)
- tells the scheduler not to schedule it. -
- ScheduleAnyway tells the scheduler to schedule
- the pod in any location, but giving higher
- precedence to topologies that would help reduce
- the skew. A constraint is considered "Unsatisfiable"
- for an incoming pod if and only if every possible
- node assigment for that pod would violate
- "MaxSkew" on some topology. For example, in
- a 3-zone cluster, MaxSkew is set to 1, and
- pods with the same labelSelector spread as
- 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P |
- If WhenUnsatisfiable is set to DoNotSchedule,
- incoming pod can only be scheduled to zone2(zone3)
- to become 3/2/1(3/1/2) as ActualSkew(2-1)
- on zone2(zone3) satisfies MaxSkew(1). In other
- words, the cluster can still be imbalanced,
- but scheduler won''t make it *more* imbalanced.
- It''s a required field.'
type: string
required:
- maxSkew
@@ -5333,590 +3126,244 @@ spec:
- whenUnsatisfiable
x-kubernetes-list-type: map
volumes:
- description: 'List of volumes that can be mounted
- by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes'
items:
- description: Volume represents a named volume in
- a pod that may be accessed by any container in
- the pod.
properties:
awsElasticBlockStore:
- description: 'AWSElasticBlockStore represents
- an AWS Disk resource that is attached to a
- kubelet''s host machine and then exposed to
- the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
partition:
- description: 'The partition in the volume
- that you want to mount. If omitted, the
- default is to mount by volume name. Examples:
- For volume /dev/sda1, you specify the
- partition as "1". Similarly, the volume
- partition for /dev/sda is "0" (or you
- can leave the property empty).'
format: int32
type: integer
readOnly:
- description: 'Specify "true" to force and
- set the ReadOnly property in VolumeMounts
- to "true". If omitted, the default is
- "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
type: boolean
volumeID:
- description: 'Unique ID of the persistent
- disk resource in AWS (Amazon EBS volume).
- More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
type: string
required:
- volumeID
type: object
azureDisk:
- description: AzureDisk represents an Azure Data
- Disk mount on the host and bind mount to the
- pod.
properties:
cachingMode:
- description: 'Host Caching mode: None, Read
- Only, Read Write.'
type: string
diskName:
- description: The Name of the data disk in
- the blob storage
type: string
diskURI:
- description: The URI the data disk in the
- blob storage
type: string
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
+ default: ext4
type: string
kind:
- description: 'Expected values Shared: multiple
- blob disks per storage account Dedicated:
- single blob disk per storage account Managed:
- azure managed data disk (only in managed
- availability set). defaults to shared'
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
+ default: false
type: boolean
required:
- diskName
- diskURI
type: object
azureFile:
- description: AzureFile represents an Azure File
- Service mount on the host and bind mount to
- the pod.
properties:
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretName:
- description: the name of secret that contains
- Azure Storage Account Name and Key
type: string
shareName:
- description: Share Name
type: string
required:
- secretName
- shareName
type: object
cephfs:
- description: CephFS represents a Ceph FS mount
- on the host that shares a pod's lifetime
properties:
monitors:
- description: 'Required: Monitors is a collection
- of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
path:
- description: 'Optional: Used as the mounted
- root, rather than the full Ceph tree,
- default is /'
type: string
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.
- More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: boolean
secretFile:
- description: 'Optional: SecretFile is the
- path to key ring for User, default is
- /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: string
secretRef:
- description: 'Optional: SecretRef is reference
- to the authentication secret for User,
- default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
user:
- description: 'Optional: User is the rados
- user name, default is admin More info:
- https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: string
required:
- monitors
type: object
cinder:
- description: 'Cinder represents a cinder volume
- attached and mounted on kubelets host machine.
- More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
properties:
fsType:
- description: 'Filesystem type to mount.
- Must be a filesystem type supported by
- the host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: string
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.
- More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: boolean
secretRef:
- description: 'Optional: points to a secret
- object containing parameters used to connect
- to OpenStack.'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
volumeID:
- description: 'volume id used to identify
- the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: string
required:
- volumeID
type: object
configMap:
- description: ConfigMap represents a configMap
- that should populate this volume
properties:
defaultMode:
- description: 'Optional: mode bits used to
- set permissions on created files by default.
- Must be an octal value between 0000 and
- 0777 or a decimal value between 0 and
- 511. YAML accepts both octal and decimal
- values, JSON requires decimal values for
- mode bits. Defaults to 0644. Directories
- within the path are not affected by this
- setting. This might be in conflict with
- other options that affect the file mode,
- like fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
items:
- description: If unspecified, each key-value
- pair in the Data field of the referenced
- ConfigMap will be projected into the volume
- as a file whose name is the key and content
- is the value. If specified, the listed
- keys will be projected into the specified
- paths, and unlisted keys will not be present.
- If a key is specified which is not present
- in the ConfigMap, the volume setup will
- error unless it is marked optional. Paths
- must be relative and may not contain the
- '..' path or start with '..'.
items:
- description: Maps a string key to a path
- within a volume.
properties:
key:
- description: The key to project.
type: string
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file. Must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: The relative path of
- the file to map the key to. May
- not be an absolute path. May not
- contain the path element '..'. May
- not start with the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent. More
- info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- or its keys must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
csi:
- description: CSI (Container Storage Interface)
- represents ephemeral storage that is handled
- by certain external CSI drivers (Beta feature).
properties:
driver:
- description: Driver is the name of the CSI
- driver that handles this volume. Consult
- with your admin for the correct name as
- registered in the cluster.
type: string
fsType:
- description: Filesystem type to mount. Ex.
- "ext4", "xfs", "ntfs". If not provided,
- the empty value is passed to the associated
- CSI driver which will determine the default
- filesystem to apply.
type: string
nodePublishSecretRef:
- description: NodePublishSecretRef is a reference
- to the secret object containing sensitive
- information to pass to the CSI driver
- to complete the CSI NodePublishVolume
- and NodeUnpublishVolume calls. This field
- is optional, and may be empty if no secret
- is required. If the secret object contains
- more than one secret, all secret references
- are passed.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
readOnly:
- description: Specifies a read-only configuration
- for the volume. Defaults to false (read/write).
type: boolean
volumeAttributes:
additionalProperties:
type: string
- description: VolumeAttributes stores driver-specific
- properties that are passed to the CSI
- driver. Consult your driver's documentation
- for supported values.
type: object
required:
- driver
type: object
downwardAPI:
- description: DownwardAPI represents downward
- API about the pod that should populate this
- volume
properties:
defaultMode:
- description: 'Optional: mode bits to use
- on created files by default. Must be a
- Optional: mode bits used to set permissions
- on created files by default. Must be an
- octal value between 0000 and 0777 or a
- decimal value between 0 and 511. YAML
- accepts both octal and decimal values,
- JSON requires decimal values for mode
- bits. Defaults to 0644. Directories within
- the path are not affected by this setting.
- This might be in conflict with other options
- that affect the file mode, like fsGroup,
- and the result can be other mode bits
- set.'
format: int32
type: integer
items:
- description: Items is a list of downward
- API volume file
items:
- description: DownwardAPIVolumeFile represents
- information to create the file containing
- the pod field
properties:
fieldRef:
- description: 'Required: Selects a
- field of the pod: only annotations,
- labels, name and namespace are supported.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file, must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: 'Required: Path is the
- relative path name of the file to
- be created. Must not be absolute
- or contain the ''..'' path. Must
- be utf-8 encoded. The first item
- of the relative path must not start
- with ''..'''
type: string
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- requests.cpu and requests.memory)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
required:
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
emptyDir:
- description: 'EmptyDir represents a temporary
- directory that shares a pod''s lifetime. More
- info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
properties:
medium:
- description: 'What type of storage medium
- should back this directory. The default
- is "" which means to use the node''s default
- medium. Must be an empty string (default)
- or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
type: string
sizeLimit:
anyOf:
- type: integer
- type: string
- description: 'Total amount of local storage
- required for this EmptyDir volume. The
- size limit is also applicable for memory
- medium. The maximum usage on memory medium
- EmptyDir would be the minimum value between
- the SizeLimit specified here and the sum
- of memory limits of all containers in
- a pod. The default is nil which means
- that the limit is undefined. More info:
- http://kubernetes.io/docs/user-guide/volumes#emptydir'
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
ephemeral:
- description: "Ephemeral represents a volume
- that is handled by a cluster storage driver
- (Alpha feature). The volume's lifecycle is
- tied to the pod that defines it - it will
- be created before the pod starts, and deleted
- when the pod is removed. \n Use this if: a)
- the volume is only needed while the pod runs,
- b) features of normal volumes like restoring
- from snapshot or capacity tracking are
- needed, c) the storage driver is specified
- through a storage class, and d) the storage
- driver supports dynamic volume provisioning
- through a PersistentVolumeClaim (see EphemeralVolumeSource
- for more information on the connection
- between this volume type and PersistentVolumeClaim).
- \n Use PersistentVolumeClaim or one of the
- vendor-specific APIs for volumes that persist
- for longer than the lifecycle of an individual
- pod. \n Use CSI for light-weight local ephemeral
- volumes if the CSI driver is meant to be used
- that way - see the documentation of the driver
- for more information. \n A pod can use both
- types of ephemeral volumes and persistent
- volumes at the same time."
properties:
- readOnly:
- description: Specifies a read-only configuration
- for the volume. Defaults to false (read/write).
- type: boolean
volumeClaimTemplate:
- description: "Will be used to create a stand-alone
- PVC to provision the volume. The pod in
- which this EphemeralVolumeSource is embedded
- will be the owner of the PVC, i.e. the
- PVC will be deleted together with the
- pod. The name of the PVC will be `-` where ``
- is the name from the `PodSpec.Volumes`
- array entry. Pod validation will reject
- the pod if the concatenated name is not
- valid for a PVC (for example, too long).
- \n An existing PVC with that name that
- is not owned by the pod will *not* be
- used for the pod to avoid using an unrelated
- volume by mistake. Starting the pod is
- then blocked until the unrelated PVC is
- removed. If such a pre-created PVC is
- meant to be used by the pod, the PVC has
- to updated with an owner reference to
- the pod once the pod exists. Normally
- this should not be necessary, but it may
- be useful when manually reconstructing
- a broken cluster. \n This field is read-only
- and no changes will be made by Kubernetes
- to the PVC after it has been created.
- \n Required, must not be nil."
properties:
metadata:
- description: May contain labels and
- annotations that will be copied into
- the PVC when creating it. No other
- fields are allowed and will be rejected
- during validation.
type: object
spec:
- description: The specification for the
- PersistentVolumeClaim. The entire
- content is copied unchanged into the
- PVC that gets created from this template.
- The same fields as in a PersistentVolumeClaim
- are also valid here.
properties:
accessModes:
- description: 'AccessModes contains
- the desired access modes the volume
- should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
dataSource:
- description: 'This field can be
- used to specify either: * An existing
- VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot
- - Beta) * An existing PVC (PersistentVolumeClaim)
- * An existing custom resource/object
- that implements data population
- (Alpha) In order to use VolumeSnapshot
- object types, the appropriate
- feature gate must be enabled (VolumeSnapshotDataSource
- or AnyVolumeDataSource) If the
- provisioner or an external controller
- can support the specified data
- source, it will create a new volume
- based on the contents of the specified
- data source. If the specified
- data source is not supported,
- the volume will not be created
- and the failure will be reported
- as an event. In the future, we
- plan to support more data source
- types and the behavior of the
- provisioner may change.'
properties:
apiGroup:
- description: APIGroup is the
- group for the resource being
- referenced. If APIGroup is
- not specified, the specified
- Kind must be in the core API
- group. For any other third-party
- types, APIGroup is required.
type: string
kind:
- description: Kind is the type
- of resource being referenced
type: string
name:
- description: Name is the name
- of resource being referenced
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
type: string
required:
- kind
- name
type: object
resources:
- description: 'Resources represents
- the minimum resources the volume
- should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
properties:
limits:
additionalProperties:
@@ -5925,10 +3372,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes
- the maximum amount of compute
- resources allowed. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -5937,96 +3380,41 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes
- the minimum amount of compute
- resources required. If Requests
- is omitted for a container,
- it defaults to Limits if that
- is explicitly specified, otherwise
- to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
selector:
- description: A label query over
- volumes to consider for binding.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
storageClassName:
- description: 'Name of the StorageClass
- required by the claim. More info:
- https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+ type: string
+ volumeAttributesClassName:
type: string
volumeMode:
- description: volumeMode defines
- what type of volume is required
- by the claim. Value of Filesystem
- is implied when not included in
- claim spec.
type: string
volumeName:
- description: VolumeName is the binding
- reference to the PersistentVolume
- backing this claim.
type: string
type: object
required:
@@ -6034,293 +3422,140 @@ spec:
type: object
type: object
fc:
- description: FC represents a Fibre Channel resource
- that is attached to a kubelet's host machine
- and then exposed to the pod.
properties:
fsType:
- description: 'Filesystem type to mount.
- Must be a filesystem type supported by
- the host operating system. Ex. "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. TODO: how do
- we prevent errors in the filesystem from
- compromising the machine'
type: string
lun:
- description: 'Optional: FC target lun number'
format: int32
type: integer
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.'
type: boolean
targetWWNs:
- description: 'Optional: FC target worldwide
- names (WWNs)'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
wwids:
- description: 'Optional: FC volume world
- wide identifiers (wwids) Either wwids
- or combination of targetWWNs and lun must
- be set, but not both simultaneously.'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
flexVolume:
- description: FlexVolume represents a generic
- volume resource that is provisioned/attached
- using an exec based plugin.
properties:
driver:
- description: Driver is the name of the driver
- to use for this volume.
type: string
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". The default filesystem depends
- on FlexVolume script.
type: string
options:
additionalProperties:
type: string
- description: 'Optional: Extra command options
- if any.'
type: object
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.'
type: boolean
secretRef:
- description: 'Optional: SecretRef is reference
- to the secret object containing sensitive
- information to pass to the plugin scripts.
- This may be empty if no secret object
- is specified. If the secret object contains
- more than one secret, all secrets are
- passed to the plugin scripts.'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
required:
- driver
type: object
flocker:
- description: Flocker represents a Flocker volume
- attached to a kubelet's host machine. This
- depends on the Flocker control service being
- running
properties:
datasetName:
- description: Name of the dataset stored
- as metadata -> name on the dataset for
- Flocker should be considered as deprecated
type: string
datasetUUID:
- description: UUID of the dataset. This is
- unique identifier of a Flocker dataset
type: string
type: object
gcePersistentDisk:
- description: 'GCEPersistentDisk represents a
- GCE Disk resource that is attached to a kubelet''s
- host machine and then exposed to the pod.
- More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
partition:
- description: 'The partition in the volume
- that you want to mount. If omitted, the
- default is to mount by volume name. Examples:
- For volume /dev/sda1, you specify the
- partition as "1". Similarly, the volume
- partition for /dev/sda is "0" (or you
- can leave the property empty). More info:
- https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
format: int32
type: integer
pdName:
- description: 'Unique name of the PD resource
- in GCE. Used to identify the disk in GCE.
- More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
type: string
readOnly:
- description: 'ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
type: boolean
required:
- pdName
type: object
gitRepo:
- description: 'GitRepo represents a git repository
- at a particular revision. DEPRECATED: GitRepo
- is deprecated. To provision a container with
- a git repo, mount an EmptyDir into an InitContainer
- that clones the repo using git, then mount
- the EmptyDir into the Pod''s container.'
properties:
directory:
- description: Target directory name. Must
- not contain or start with '..'. If '.'
- is supplied, the volume directory will
- be the git repository. Otherwise, if
- specified, the volume will contain the
- git repository in the subdirectory with
- the given name.
type: string
repository:
- description: Repository URL
type: string
revision:
- description: Commit hash for the specified
- revision.
type: string
required:
- repository
type: object
glusterfs:
- description: 'Glusterfs represents a Glusterfs
- mount on the host that shares a pod''s lifetime.
- More info: https://examples.k8s.io/volumes/glusterfs/README.md'
properties:
endpoints:
- description: 'EndpointsName is the endpoint
- name that details Glusterfs topology.
- More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: string
path:
- description: 'Path is the Glusterfs volume
- path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: string
readOnly:
- description: 'ReadOnly here will force the
- Glusterfs volume to be mounted with read-only
- permissions. Defaults to false. More info:
- https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: boolean
required:
- endpoints
- path
type: object
hostPath:
- description: 'HostPath represents a pre-existing
- file or directory on the host machine that
- is directly exposed to the container. This
- is generally used for system agents or other
- privileged things that are allowed to see
- the host machine. Most containers will NOT
- need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
- --- TODO(jonesdl) We need to restrict who
- can use host directory mounts and who can/can
- not mount host directories as read/write.'
properties:
path:
- description: 'Path of the directory on the
- host. If the path is a symlink, it will
- follow the link to the real path. More
- info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
type: string
type:
- description: 'Type for HostPath Volume Defaults
- to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
type: string
required:
- path
type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
iscsi:
- description: 'ISCSI represents an ISCSI Disk
- resource that is attached to a kubelet''s
- host machine and then exposed to the pod.
- More info: https://examples.k8s.io/volumes/iscsi/README.md'
properties:
chapAuthDiscovery:
- description: whether support iSCSI Discovery
- CHAP authentication
type: boolean
chapAuthSession:
- description: whether support iSCSI Session
- CHAP authentication
type: boolean
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
initiatorName:
- description: Custom iSCSI Initiator Name.
- If initiatorName is specified with iscsiInterface
- simultaneously, new iSCSI interface : will be created
- for the connection.
type: string
iqn:
- description: Target iSCSI Qualified Name.
type: string
iscsiInterface:
- description: iSCSI Interface Name that uses
- an iSCSI transport. Defaults to 'default'
- (tcp).
+ default: default
type: string
lun:
- description: iSCSI Target Lun number.
format: int32
type: integer
portals:
- description: iSCSI Target Portal List. The
- portal is either an IP or ip_addr:port
- if the port is other than default (typically
- TCP ports 860 and 3260).
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
readOnly:
- description: ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false.
type: boolean
secretRef:
- description: CHAP Secret for iSCSI target
- and initiator authentication
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
targetPortal:
- description: iSCSI Target Portal. The Portal
- is either an IP or ip_addr:port if the
- port is other than default (typically
- TCP ports 860 and 3260).
type: string
required:
- iqn
@@ -6328,577 +3563,301 @@ spec:
- targetPortal
type: object
name:
- description: 'Volume''s name. Must be a DNS_LABEL
- and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
nfs:
- description: 'NFS represents an NFS mount on
- the host that shares a pod''s lifetime More
- info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
properties:
path:
- description: 'Path that is exported by the
- NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: string
readOnly:
- description: 'ReadOnly here will force the
- NFS export to be mounted with read-only
- permissions. Defaults to false. More info:
- https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: boolean
server:
- description: 'Server is the hostname or
- IP address of the NFS server. More info:
- https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: string
required:
- path
- server
type: object
persistentVolumeClaim:
- description: 'PersistentVolumeClaimVolumeSource
- represents a reference to a PersistentVolumeClaim
- in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
properties:
claimName:
- description: 'ClaimName is the name of a
- PersistentVolumeClaim in the same namespace
- as the pod using this volume. More info:
- https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
type: string
readOnly:
- description: Will force the ReadOnly setting
- in VolumeMounts. Default false.
type: boolean
required:
- claimName
type: object
photonPersistentDisk:
- description: PhotonPersistentDisk represents
- a PhotonController persistent disk attached
- and mounted on kubelets host machine
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
pdID:
- description: ID that identifies Photon Controller
- persistent disk
type: string
required:
- pdID
type: object
portworxVolume:
- description: PortworxVolume represents a portworx
- volume attached and mounted on kubelets host
- machine
properties:
fsType:
- description: FSType represents the filesystem
- type to mount Must be a filesystem type
- supported by the host operating system.
- Ex. "ext4", "xfs". Implicitly inferred
- to be "ext4" if unspecified.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
volumeID:
- description: VolumeID uniquely identifies
- a Portworx volume
type: string
required:
- volumeID
type: object
projected:
- description: Items for all in one resources
- secrets, configmaps, and downward API
properties:
defaultMode:
- description: Mode bits used to set permissions
- on created files by default. Must be an
- octal value between 0000 and 0777 or a
- decimal value between 0 and 511. YAML
- accepts both octal and decimal values,
- JSON requires decimal values for mode
- bits. Directories within the path are
- not affected by this setting. This might
- be in conflict with other options that
- affect the file mode, like fsGroup, and
- the result can be other mode bits set.
format: int32
type: integer
sources:
- description: list of volume projections
items:
- description: Projection that may be projected
- along with other supported volume types
properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
configMap:
- description: information about the
- configMap data to project
properties:
items:
- description: If unspecified, each
- key-value pair in the Data field
- of the referenced ConfigMap
- will be projected into the volume
- as a file whose name is the
- key and content is the value.
- If specified, the listed keys
- will be projected into the specified
- paths, and unlisted keys will
- not be present. If a key is
- specified which is not present
- in the ConfigMap, the volume
- setup will error unless it is
- marked optional. Paths must
- be relative and may not contain
- the '..' path or start with
- '..'.
items:
- description: Maps a string key
- to a path within a volume.
properties:
key:
- description: The key to
- project.
type: string
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file.
- Must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: The relative
- path of the file to map
- the key to. May not be
- an absolute path. May
- not contain the path element
- '..'. May not start with
- the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its keys must be
- defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
downwardAPI:
- description: information about the
- downwardAPI data to project
properties:
items:
- description: Items is a list of
- DownwardAPIVolume file
items:
- description: DownwardAPIVolumeFile
- represents information to
- create the file containing
- the pod field
properties:
fieldRef:
- description: 'Required:
- Selects a field of the
- pod: only annotations,
- labels, name and namespace
- are supported.'
properties:
apiVersion:
- description: Version
- of the schema the
- FieldPath is written
- in terms of, defaults
- to "v1".
type: string
fieldPath:
- description: Path of
- the field to select
- in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file,
- must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: 'Required:
- Path is the relative
- path name of the file
- to be created. Must not
- be absolute or contain
- the ''..'' path. Must
- be utf-8 encoded. The
- first item of the relative
- path must not start with
- ''..'''
type: string
resourceFieldRef:
- description: 'Selects a
- resource of the container:
- only resources limits
- and requests (limits.cpu,
- limits.memory, requests.cpu
- and requests.memory) are
- currently supported.'
properties:
containerName:
- description: 'Container
- name: required for
- volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies
- the output format
- of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required:
- resource to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
required:
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
type: object
secret:
- description: information about the
- secret data to project
properties:
items:
- description: If unspecified, each
- key-value pair in the Data field
- of the referenced Secret will
- be projected into the volume
- as a file whose name is the
- key and content is the value.
- If specified, the listed keys
- will be projected into the specified
- paths, and unlisted keys will
- not be present. If a key is
- specified which is not present
- in the Secret, the volume setup
- will error unless it is marked
- optional. Paths must be relative
- and may not contain the '..'
- path or start with '..'.
items:
- description: Maps a string key
- to a path within a volume.
properties:
key:
- description: The key to
- project.
type: string
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file.
- Must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: The relative
- path of the file to map
- the key to. May not be
- an absolute path. May
- not contain the path element
- '..'. May not start with
- the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
serviceAccountToken:
- description: information about the
- serviceAccountToken data to project
properties:
audience:
- description: Audience is the intended
- audience of the token. A recipient
- of a token must identify itself
- with an identifier specified
- in the audience of the token,
- and otherwise should reject
- the token. The audience defaults
- to the identifier of the apiserver.
type: string
expirationSeconds:
- description: ExpirationSeconds
- is the requested duration of
- validity of the service account
- token. As the token approaches
- expiration, the kubelet volume
- plugin will proactively rotate
- the service account token. The
- kubelet will start trying to
- rotate the token if the token
- is older than 80 percent of
- its time to live or if the token
- is older than 24 hours.Defaults
- to 1 hour and must be at least
- 10 minutes.
format: int64
type: integer
path:
- description: Path is the path
- relative to the mount point
- of the file to project the token
- into.
type: string
required:
- path
type: object
type: object
type: array
- required:
- - sources
+ x-kubernetes-list-type: atomic
type: object
quobyte:
- description: Quobyte represents a Quobyte mount
- on the host that shares a pod's lifetime
properties:
group:
- description: Group to map volume access
- to Default is no group
type: string
readOnly:
- description: ReadOnly here will force the
- Quobyte volume to be mounted with read-only
- permissions. Defaults to false.
type: boolean
registry:
- description: Registry represents a single
- or multiple Quobyte Registry services
- specified as a string as host:port pair
- (multiple entries are separated with commas)
- which acts as the central registry for
- volumes
type: string
tenant:
- description: Tenant owning the given Quobyte
- volume in the Backend Used with dynamically
- provisioned Quobyte volumes, value is
- set by the plugin
type: string
user:
- description: User to map volume access to
- Defaults to serivceaccount user
type: string
volume:
- description: Volume is a string that references
- an already created Quobyte volume by name.
type: string
required:
- registry
- volume
type: object
rbd:
- description: 'RBD represents a Rados Block Device
- mount on the host that shares a pod''s lifetime.
- More info: https://examples.k8s.io/volumes/rbd/README.md'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
image:
- description: 'The rados image name. More
- info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
type: string
keyring:
- description: 'Keyring is the path to key
- ring for RBDUser. Default is /etc/ceph/keyring.
- More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: /etc/ceph/keyring
type: string
monitors:
- description: 'A collection of Ceph monitors.
- More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
pool:
- description: 'The rados pool name. Default
- is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: rbd
type: string
readOnly:
- description: 'ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
type: boolean
secretRef:
- description: 'SecretRef is name of the authentication
- secret for RBDUser. If provided overrides
- keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
user:
- description: 'The rados user name. Default
- is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: admin
type: string
required:
- image
- monitors
type: object
scaleIO:
- description: ScaleIO represents a ScaleIO persistent
- volume attached and mounted on Kubernetes
- nodes.
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Default is "xfs".
+ default: xfs
type: string
gateway:
- description: The host address of the ScaleIO
- API Gateway.
type: string
protectionDomain:
- description: The name of the ScaleIO Protection
- Domain for the configured storage.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretRef:
- description: SecretRef references to the
- secret for ScaleIO user and other sensitive
- information. If this is not provided,
- Login operation will fail.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
sslEnabled:
- description: Flag to enable/disable SSL
- communication with Gateway, default false
type: boolean
storageMode:
- description: Indicates whether the storage
- for a volume should be ThickProvisioned
- or ThinProvisioned. Default is ThinProvisioned.
+ default: ThinProvisioned
type: string
storagePool:
- description: The ScaleIO Storage Pool associated
- with the protection domain.
type: string
system:
- description: The name of the storage system
- as configured in ScaleIO.
type: string
volumeName:
- description: The name of a volume already
- created in the ScaleIO system that is
- associated with this volume source.
type: string
required:
- gateway
@@ -6906,154 +3865,58 @@ spec:
- system
type: object
secret:
- description: 'Secret represents a secret that
- should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
properties:
defaultMode:
- description: 'Optional: mode bits used to
- set permissions on created files by default.
- Must be an octal value between 0000 and
- 0777 or a decimal value between 0 and
- 511. YAML accepts both octal and decimal
- values, JSON requires decimal values for
- mode bits. Defaults to 0644. Directories
- within the path are not affected by this
- setting. This might be in conflict with
- other options that affect the file mode,
- like fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
items:
- description: If unspecified, each key-value
- pair in the Data field of the referenced
- Secret will be projected into the volume
- as a file whose name is the key and content
- is the value. If specified, the listed
- keys will be projected into the specified
- paths, and unlisted keys will not be present.
- If a key is specified which is not present
- in the Secret, the volume setup will error
- unless it is marked optional. Paths must
- be relative and may not contain the '..'
- path or start with '..'.
items:
- description: Maps a string key to a path
- within a volume.
properties:
key:
- description: The key to project.
type: string
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file. Must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: The relative path of
- the file to map the key to. May
- not be an absolute path. May not
- contain the path element '..'. May
- not start with the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
optional:
- description: Specify whether the Secret
- or its keys must be defined
type: boolean
secretName:
- description: 'Name of the secret in the
- pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
type: string
type: object
storageos:
- description: StorageOS represents a StorageOS
- volume attached and mounted on Kubernetes
- nodes.
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretRef:
- description: SecretRef specifies the secret
- to use for obtaining the StorageOS API
- credentials. If not specified, default
- values will be attempted.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
volumeName:
- description: VolumeName is the human-readable
- name of the StorageOS volume. Volume
- names are only unique within a namespace.
type: string
volumeNamespace:
- description: VolumeNamespace specifies the
- scope of the volume within StorageOS. If
- no namespace is specified then the Pod's
- namespace will be used. This allows the
- Kubernetes name scoping to be mirrored
- within StorageOS for tighter integration.
- Set VolumeName to any name to override
- the default behaviour. Set to "default"
- if you are not using namespaces within
- StorageOS. Namespaces that do not pre-exist
- within StorageOS will be created.
type: string
type: object
vsphereVolume:
- description: VsphereVolume represents a vSphere
- volume attached and mounted on kubelets host
- machine
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
storagePolicyID:
- description: Storage Policy Based Management
- (SPBM) profile ID associated with the
- StoragePolicyName.
type: string
storagePolicyName:
- description: Storage Policy Based Management
- (SPBM) profile name.
type: string
volumePath:
- description: Path that identifies vSphere
- volume vmdk
type: string
required:
- volumePath
@@ -7062,21 +3925,14 @@ spec:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
required:
- containers
type: object
type: object
ttlSecondsAfterFinished:
- description: ttlSecondsAfterFinished limits the lifetime of
- a Job that has finished execution (either Complete or Failed).
- If this field is set, ttlSecondsAfterFinished after the
- Job finishes, it is eligible to be automatically deleted.
- When the Job is being deleted, its lifecycle guarantees
- (e.g. finalizers) will be honored. If this field is unset,
- the Job won't be automatically deleted. If this field is
- set to zero, the Job becomes eligible to be deleted immediately
- after it finishes. This field is alpha-level and is only
- honored by servers that enable the TTLAfterFinished feature.
format: int32
type: integer
required:
@@ -7084,111 +3940,94 @@ spec:
type: object
type: object
schedule:
- description: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
minLength: 0
type: string
startingDeadlineSeconds:
- description: Optional deadline in seconds for starting the job if
- it misses scheduled time for any reason. Missed jobs executions
- will be counted as failed ones.
format: int64
minimum: 0
type: integer
successfulJobsHistoryLimit:
- description: The number of successful finished jobs to retain. This
- is a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
suspend:
- description: This flag tells the controller to suspend subsequent
- executions, it does not apply to already started executions. Defaults
- to false.
type: boolean
required:
- jobTemplate
- schedule
type: object
status:
- description: CronJobStatus defines the observed state of CronJob
properties:
active:
- description: A list of pointers to currently running jobs.
items:
- description: 'ObjectReference contains enough information to let
- you inspect or modify the referred object. --- New uses of this
- type are discouraged because of difficulty describing its usage
- when embedded in APIs. 1. Ignored fields. It includes many fields
- which are not generally honored. For instance, ResourceVersion
- and FieldPath are both very rarely valid in actual usage. 2.
- Invalid usage help. It is impossible to add specific help for
- individual usage. In most embedded usages, there are particular restrictions
- like, "must refer only to types A and B" or "UID not honored"
- or "name must be restricted". Those cannot be well described
- when embedded. 3. Inconsistent validation. Because the usages
- are different, the validation rules are different by usage, which
- makes it hard for users to predict what will happen. 4. The fields
- are both imprecise and overly precise. Kind is not a precise
- mapping to a URL. This can produce ambiguity during interpretation
- and require a REST mapping. In most cases, the dependency is
- on the group,resource tuple and the version of the actual
- struct is irrelevant. 5. We cannot easily change it. Because
- this type is embedded in many locations, updates to this type will
- affect numerous schemas. Don''t make new APIs embed an underspecified
- API type they do not control. Instead of using this type, create
- a locally provided and used type that is well-focused on your
- reference. For example, ServiceReferences for admission registration:
- https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533
- .'
properties:
apiVersion:
- description: API version of the referent.
type: string
fieldPath:
- description: 'If referring to a piece of an object instead of
- an entire object, this string should contain a valid JSON/Go
- field access statement, such as desiredState.manifest.containers[2].
- For example, if the object reference is to a container within
- a pod, this would take on a value like: "spec.containers{name}"
- (where "name" refers to the name of the container that triggered
- the event) or if no container name is specified "spec.containers[2]"
- (container with index 2 in this pod). This syntax is chosen
- only to have some well-defined way of referencing a part of
- an object. TODO: this design is not final and this field is
- subject to change in the future.'
type: string
kind:
- description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
- description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
- description: 'Specific resourceVersion to which this reference
- is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
lastScheduleTime:
- description: Information when was the last time the job was successfully
- scheduled.
format: date-time
type: string
type: object
+ required:
+ - spec
type: object
served: true
storage: true
subresources:
status: {}
-status:
- acceptedNames:
- kind: ""
- plural: ""
- conditions: []
- storedVersions: []
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 bcc0a355f28..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
@@ -3,19 +3,14 @@
# It should be run by config/default
resources:
- bases/batch.tutorial.kubebuilder.io_cronjobs.yaml
-#+kubebuilder:scaffold:crdkustomizeresource
+# +kubebuilder:scaffold:crdkustomizeresource
-patchesStrategicMerge:
+patches:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
-#- patches/webhook_in_cronjobs.yaml
-#+kubebuilder:scaffold:crdkustomizewebhookpatch
-
-# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
-# patches here are for enabling the CA injection for each CRD
-#- patches/cainjection_in_cronjobs.yaml
-#+kubebuilder:scaffold:crdkustomizecainjectionpatch
+# +kubebuilder:scaffold:crdkustomizewebhookpatch
+# [WEBHOOK] To enable webhook, uncomment the following section
# the following config is for teaching kustomize how to do kustomization for CRDs.
-configurations:
-- kustomizeconfig.yaml
+#configurations:
+#- kustomizeconfig.yaml
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/crd/patches/cainjection_in_cronjobs.yaml
deleted file mode 100644
index 7b037c0e41f..00000000000
--- a/docs/book/src/cronjob-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/cronjob-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
deleted file mode 100644
index 76d1d9a3744..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-# The following patch enables a conversion webhook for the CRD
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
- name: cronjobs.batch.tutorial.kubebuilder.io
-spec:
- conversion:
- strategy: Webhook
- webhook:
- clientConfig:
- service:
- namespace: system
- name: webhook-service
- path: /convert
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 788c15ac747..53274978801 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
@@ -9,66 +9,226 @@ namespace: project-system
namePrefix: project-
# Labels to add to all resources and selectors.
-#commonLabels:
-# someName: someValue
+#labels:
+#- includeSelectors: true
+# pairs:
+# someName: someValue
-bases:
+resources:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
-#- ../webhook
+- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
-#- ../certmanager
+- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
-#- ../prometheus
+- ../prometheus
+# [METRICS] Expose the controller manager metrics service.
+- metrics_service.yaml
+# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
+# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
+# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will
+# be able to communicate with the Webhook Server.
+#- ../network-policy
-patchesStrategicMerge:
-# Protect the /metrics endpoint by putting it behind auth.
-# If you want your controller-manager to expose the /metrics
-# endpoint w/o any authn/z, please comment the following line.
-- manager_auth_proxy_patch.yaml
+# 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
+- path: manager_metrics_patch.yaml
+ target:
+ kind: Deployment
-# Mount the controller config file for loading manager configurations
-# through a ComponentConfig type
-#- manager_config_patch.yaml
+# 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
-#- manager_webhook_patch.yaml
+- path: manager_webhook_patch.yaml
+ target:
+ kind: Deployment
-# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
-# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
-# 'CERTMANAGER' needs to be enabled to use ca injection
-#- webhookcainjection_patch.yaml
-
-# the following config is for teaching kustomize how to do var substitution
-vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
-- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
- objref:
- kind: Certificate
- group: cert-manager.io
- version: v1
- name: serving-cert # this name should match the one in certificate.yaml
- fieldref:
- fieldpath: metadata.namespace
-- name: CERTIFICATE_NAME
- objref:
- kind: Certificate
- group: cert-manager.io
- version: v1
- name: serving-cert # this name should match the one in certificate.yaml
-- name: SERVICE_NAMESPACE # namespace of the service
- objref:
- kind: Service
- version: v1
- name: webhook-service
- fieldref:
- fieldpath: metadata.namespace
-- name: SERVICE_NAME
- objref:
- kind: Service
- version: v1
- name: webhook-service
+# 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
+ - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor
+ kind: ServiceMonitor
+ group: monitoring.coreos.com
+ version: v1
+ name: controller-manager-metrics-monitor
+ fieldPaths:
+ - spec.endpoints.0.tlsConfig.serverName
+ 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
+ - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor
+ kind: ServiceMonitor
+ group: monitoring.coreos.com
+ version: v1
+ name: controller-manager-metrics-monitor
+ fieldPaths:
+ - spec.endpoints.0.tlsConfig.serverName
+ options:
+ delimiter: '.'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have any webhook
+ kind: Service
+ version: v1
+ name: webhook-service
+ fieldPath: .metadata.name # Name of the service
+ targets:
+ - select:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPaths:
+ - .spec.dnsNames.0
+ - .spec.dnsNames.1
+ options:
+ delimiter: '.'
+ index: 0
+ create: true
+ - source:
+ kind: Service
+ version: v1
+ name: webhook-service
+ fieldPath: .metadata.namespace # Namespace of the service
+ targets:
+ - select:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPaths:
+ - .spec.dnsNames.0
+ - .spec.dnsNames.1
+ options:
+ delimiter: '.'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation)
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert # This name should match the one in certificate.yaml
+ fieldPath: .metadata.namespace # Namespace of the certificate CR
+ targets:
+ - select:
+ kind: ValidatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 0
+ create: true
+ - source:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.name
+ targets:
+ - select:
+ kind: ValidatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting )
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.namespace # Namespace of the certificate CR
+ targets:
+ - select:
+ kind: MutatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 0
+ create: true
+ - source:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.name
+ targets:
+ - select:
+ kind: MutatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 1
+ create: true
+
+# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPath: .metadata.namespace # Namespace of the certificate CR
+# 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
+# fieldPath: .metadata.name
+# 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_auth_proxy_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml
deleted file mode 100644
index a224be19ea1..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-# This patch inject a sidecar container which is a HTTP proxy for the
-# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-spec:
- template:
- spec:
- containers:
- - name: kube-rbac-proxy
- image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
- args:
- - "--secure-listen-address=0.0.0.0:8443"
- - "--upstream=http://127.0.0.1:8080/"
- - "--logtostderr=true"
- - "--v=10"
- ports:
- - containerPort: 8443
- name: https
- - name: manager
- args:
- - "--health-probe-bind-address=:8081"
- - "--metrics-bind-address=127.0.0.1:8080"
- - "--leader-elect"
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_config_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_config_patch.yaml
deleted file mode 100644
index 6c400155cfb..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_config_patch.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-spec:
- template:
- spec:
- containers:
- - name: manager
- args:
- - "--config=controller_manager_config.yaml"
- volumeMounts:
- - name: manager-config
- mountPath: /controller_manager_config.yaml
- subPath: controller_manager_config.yaml
- volumes:
- - name: manager-config
- configMap:
- name: manager-config
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_metrics_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_metrics_patch.yaml
new file mode 100644
index 00000000000..2aaef6536f4
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/default/manager_metrics_patch.yaml
@@ -0,0 +1,4 @@
+# This patch adds the args to allow exposing the metrics endpoint using HTTPS
+- op: add
+ path: /spec/template/spec/containers/0/args/0
+ value: --metrics-bind-address=:8443
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 738de350b71..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,23 +1,31 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-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
new file mode 100644
index 00000000000..df85a7387c5
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/default/metrics_service.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: controller-manager-metrics-service
+ namespace: system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: 8443
+ selector:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml
deleted file mode 100644
index 02ab515d428..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# This patch add annotation to admission webhook config and
-# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
-apiVersion: admissionregistration.k8s.io/v1
-kind: MutatingWebhookConfiguration
-metadata:
- name: mutating-webhook-configuration
- annotations:
- cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
----
-apiVersion: admissionregistration.k8s.io/v1
-kind: ValidatingWebhookConfiguration
-metadata:
- name: validating-webhook-configuration
- annotations:
- cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/manager/controller_manager_config.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/manager/controller_manager_config.yaml
deleted file mode 100644
index 08498b82e89..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/manager/controller_manager_config.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
-kind: ControllerManagerConfig
-health:
- healthProbeBindAddress: :8081
-metrics:
- bindAddress: 127.0.0.1:8080
-webhook:
- port: 9443
-leaderElection:
- leaderElect: true
- resourceName: 80807133.tutorial.kubebuilder.io
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/manager/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/manager/kustomization.yaml
index 2bcd3eeaa94..ad13e96b3fc 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/manager/kustomization.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/manager/kustomization.yaml
@@ -1,10 +1,8 @@
resources:
- manager.yaml
-
-generatorOptions:
- disableNameSuffixHash: true
-
-configMapGenerator:
-- name: manager-config
- files:
- - controller_manager_config.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+images:
+- name: controller
+ newName: controller
+ newTag: latest
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 79adfe72a55..8fb2249a918 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
@@ -3,6 +3,8 @@ kind: Namespace
metadata:
labels:
control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: system
---
apiVersion: apps/v1
@@ -12,27 +14,64 @@ metadata:
namespace: system
labels:
control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
spec:
selector:
matchLabels:
control-plane: controller-manager
+ app.kubernetes.io/name: project
replicas: 1
template:
metadata:
+ annotations:
+ 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.
+ # It is considered best practice to support multiple architectures. You can
+ # build your manager image using the makefile target docker-buildx.
+ # affinity:
+ # nodeAffinity:
+ # requiredDuringSchedulingIgnoredDuringExecution:
+ # nodeSelectorTerms:
+ # - matchExpressions:
+ # - key: kubernetes.io/arch
+ # operator: In
+ # values:
+ # - amd64
+ # - arm64
+ # - ppc64le
+ # - s390x
+ # - key: kubernetes.io/os
+ # operator: In
+ # 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
+ seccompProfile:
+ type: RuntimeDefault
containers:
- command:
- /manager
args:
- - --leader-elect
+ - --leader-elect
+ - --health-probe-bind-address=:8081
image: controller:latest
name: manager
+ ports: []
securityContext:
+ readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - "ALL"
livenessProbe:
httpGet:
path: /healthz
@@ -45,12 +84,16 @@ spec:
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
+ # TODO(user): Configure the resources accordingly based on the project requirements.
+ # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
resources:
limits:
- cpu: 100m
- memory: 30Mi
+ cpu: 500m
+ memory: 128Mi
requests:
- cpu: 100m
- memory: 20Mi
+ 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
new file mode 100644
index 00000000000..0e64d74ef6e
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml
@@ -0,0 +1,27 @@
+# This NetworkPolicy allows ingress traffic
+# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
+# namespaces are able to gather data from the metrics endpoint.
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: allow-metrics-traffic
+ namespace: system
+spec:
+ podSelector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ policyTypes:
+ - Ingress
+ ingress:
+ # This allows ingress traffic from any namespace with the label metrics: enabled
+ - from:
+ - namespaceSelector:
+ matchLabels:
+ metrics: enabled # Only from namespaces with this label
+ ports:
+ - port: 8443
+ protocol: TCP
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
new file mode 100644
index 00000000000..eb9464b64ea
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml
@@ -0,0 +1,27 @@
+# This NetworkPolicy allows ingress traffic to your webhook server running
+# as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks
+# will only work when applied in namespaces labeled with 'webhook: enabled'
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: allow-webhook-traffic
+ namespace: system
+spec:
+ podSelector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ policyTypes:
+ - Ingress
+ ingress:
+ # This allows ingress traffic from any namespace with the label webhook: enabled
+ - from:
+ - namespaceSelector:
+ matchLabels:
+ webhook: enabled # Only from namespaces with this label
+ ports:
+ - port: 443
+ protocol: TCP
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/kustomization.yaml
new file mode 100644
index 00000000000..0872bee124c
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/network-policy/kustomization.yaml
@@ -0,0 +1,3 @@
+resources:
+- allow-webhook-traffic.yaml
+- allow-metrics-traffic.yaml
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 d19136ae710..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
@@ -1,20 +1,27 @@
-
# Prometheus Monitor Service (Metrics)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: controller-manager-metrics-monitor
namespace: system
spec:
endpoints:
- path: /metrics
- port: https
+ port: https # Ensure this is the name of the port that exposes HTTPS metrics
scheme: https
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, 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..5bf84ce0d53
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml
@@ -0,0 +1,19 @@
+# Patch for Prometheus ServiceMonitor to enable secure TLS configuration
+# using certificates managed by cert-manager
+- op: replace
+ path: /spec/endpoints/0/tlsConfig
+ value:
+ # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
+ serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
+ 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/auth_proxy_client_clusterrole.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_client_clusterrole.yaml
deleted file mode 100644
index bd4af137a9f..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_client_clusterrole.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: metrics-reader
-rules:
-- nonResourceURLs: ["/metrics"]
- verbs: ["get"]
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_role.yaml
deleted file mode 100644
index 618f5e4177c..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_role.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
- name: proxy-role
-rules:
-- apiGroups: ["authentication.k8s.io"]
- resources:
- - tokenreviews
- verbs: ["create"]
-- apiGroups: ["authorization.k8s.io"]
- resources:
- - subjectaccessreviews
- verbs: ["create"]
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_role_binding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_role_binding.yaml
deleted file mode 100644
index ec7acc0a1b7..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_role_binding.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: proxy-rolebinding
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: proxy-role
-subjects:
-- kind: ServiceAccount
- name: controller-manager
- namespace: system
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_service.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_service.yaml
deleted file mode 100644
index 6cf656be149..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/auth_proxy_service.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- labels:
- control-plane: controller-manager
- name: controller-manager-metrics-service
- namespace: system
-spec:
- ports:
- - name: https
- port: 8443
- targetPort: https
- selector:
- control-plane: controller-manager
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 19ab4dad440..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,7 +1,16 @@
-# 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:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: cronjob-editor-role
rules:
- apiGroups:
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 f31d815f94e..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,7 +1,16 @@
-# 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:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: cronjob-viewer-role
rules:
- apiGroups:
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 731832a6ac3..097aba38485 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
@@ -9,10 +9,20 @@ resources:
- role_binding.yaml
- leader_election_role.yaml
- leader_election_role_binding.yaml
-# Comment the following 4 lines if you want to disable
-# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
-# which protects your /metrics endpoint.
-- auth_proxy_service.yaml
-- auth_proxy_role.yaml
-- auth_proxy_role_binding.yaml
-- auth_proxy_client_clusterrole.yaml
+# The following RBAC configurations are used to protect
+# the metrics endpoint with authn/authz. These configurations
+# ensure that only authorized users and service accounts
+# can access the metrics endpoint. Comment the following
+# permissions if you want to disable this protection.
+# More info: https://book.kubebuilder.io/reference/metrics.html
+- metrics_auth_role.yaml
+- metrics_auth_role_binding.yaml
+- metrics_reader_role.yaml
+# 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
+# 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/rbac/leader_election_role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role.yaml
index 6334cc51c83..e3fc403c0d9 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role.yaml
@@ -2,13 +2,26 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: leader-election-role
rules:
- apiGroups:
- ""
- - coordination.k8s.io
resources:
- configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
- leases
verbs:
- get
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
index 1d1321ed4f0..133026ff212 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
@@ -1,6 +1,9 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: leader-election-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_auth_role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_auth_role.yaml
new file mode 100644
index 00000000000..32d2e4ec6b0
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_auth_role.yaml
@@ -0,0 +1,17 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: metrics-auth-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_auth_role_binding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_auth_role_binding.yaml
new file mode 100644
index 00000000000..e775d67ff08
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_auth_role_binding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: metrics-auth-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
diff --git a/testdata/project-v3-addon/config/rbac/auth_proxy_client_clusterrole.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_reader_role.yaml
similarity index 100%
rename from testdata/project-v3-addon/config/rbac/auth_proxy_client_clusterrole.yaml
rename to docs/book/src/cronjob-tutorial/testdata/project/config/rbac/metrics_reader_role.yaml
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role.yaml
index 097ef35346f..8f87d954c79 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role.yaml
@@ -1,9 +1,7 @@
-
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
- creationTimestamp: null
name: manager-role
rules:
- apiGroups:
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role_binding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role_binding.yaml
index 2070ede4462..1e81c2443c8 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role_binding.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/role_binding.yaml
@@ -1,6 +1,9 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/service_account.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/service_account.yaml
index 7cd6025bfc4..7733e7fc663 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/service_account.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/rbac/service_account.yaml
@@ -1,5 +1,8 @@
apiVersion: v1
kind: ServiceAccount
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: controller-manager
namespace: system
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
index 12d70ccf68f..dbf5df5e8d9 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
@@ -1,6 +1,9 @@
apiVersion: batch.tutorial.kubebuilder.io/v1
kind: CronJob
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: cronjob-sample
spec:
schedule: "*/1 * * * *"
@@ -10,6 +13,11 @@ spec:
spec:
template:
spec:
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ seccompProfile:
+ type: RuntimeDefault
containers:
- name: hello
image: busybox
@@ -17,4 +25,11 @@ spec:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: false
restartPolicy: OnFailure
+
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/samples/kustomization.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/samples/kustomization.yaml
new file mode 100644
index 00000000000..fb14a1bedc9
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/samples/kustomization.yaml
@@ -0,0 +1,4 @@
+## Append samples of your project ##
+resources:
+- batch_v1_cronjob.yaml
+# +kubebuilder:scaffold:manifestskustomizesamples
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
index 25e21e3c963..206316e54ff 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
@@ -1,4 +1,4 @@
-# the following config is for teaching kustomize where to look at when substituting vars.
+# the following config is for teaching kustomize where to look at when substituting nameReference.
# It requires kustomize v2.1.0 or newer to work properly.
nameReference:
- kind: Service
@@ -20,6 +20,3 @@ namespace:
group: admissionregistration.k8s.io
path: webhooks/clientConfig/service/namespace
create: true
-
-varReference:
-- path: metadata/annotations
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/manifests.yaml b/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/manifests.yaml
index 20849c6f1d6..92fc8109797 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/manifests.yaml
+++ b/docs/book/src/cronjob-tutorial/testdata/project/config/webhook/manifests.yaml
@@ -1,9 +1,7 @@
-
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
- creationTimestamp: null
name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
@@ -14,7 +12,7 @@ webhooks:
namespace: system
path: /mutate-batch-tutorial-kubebuilder-io-v1-cronjob
failurePolicy: Fail
- name: mcronjob.kb.io
+ name: mcronjob-v1.kb.io
rules:
- apiGroups:
- batch.tutorial.kubebuilder.io
@@ -26,12 +24,10 @@ webhooks:
resources:
- cronjobs
sideEffects: None
-
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
- creationTimestamp: null
name: validating-webhook-configuration
webhooks:
- admissionReviewVersions:
@@ -42,7 +38,7 @@ webhooks:
namespace: system
path: /validate-batch-tutorial-kubebuilder-io-v1-cronjob
failurePolicy: Fail
- name: vcronjob.kb.io
+ name: vcronjob-v1.kb.io
rules:
- apiGroups:
- batch.tutorial.kubebuilder.io
@@ -51,7 +47,6 @@ webhooks:
operations:
- CREATE
- UPDATE
- - DELETE
resources:
- cronjobs
sideEffects: None
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 31e0f829591..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
@@ -1,12 +1,16 @@
-
apiVersion: v1
kind: Service
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: webhook-service
namespace: system
spec:
ports:
- port: 443
+ protocol: TCP
targetPort: 9443
selector:
control-plane: controller-manager
+ app.kubernetes.io/name: project
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/controllers/cronjob_controller.go b/docs/book/src/cronjob-tutorial/testdata/project/controllers/cronjob_controller.go
deleted file mode 100644
index b37f13fbbaf..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/controllers/cronjob_controller.go
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-/*
-We'll start out with some imports. You'll see below that we'll need a few more imports
-than those scaffolded for us. We'll talk about each one when we use it.
-*/
-package controllers
-
-import (
- "context"
- "fmt"
- "sort"
- "time"
-
- "github.com/go-logr/logr"
- "github.com/robfig/cron"
- kbatch "k8s.io/api/batch/v1"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- ref "k8s.io/client-go/tools/reference"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
-
- batch "tutorial.kubebuilder.io/project/api/v1"
-)
-
-/*
-Next, we'll need a Clock, which will allow us to fake timing in our tests.
-*/
-
-// CronJobReconciler reconciles a CronJob object
-type CronJobReconciler struct {
- client.Client
- Log logr.Logger
- Scheme *runtime.Scheme
- Clock
-}
-
-/*
-We'll mock out the clock to make it easier to jump around in time while testing,
-the "real" clock just calls `time.Now`.
-*/
-type realClock struct{}
-
-func (_ realClock) Now() time.Time { return time.Now() }
-
-// clock knows how to get the current time.
-// It can be used to fake out timing for testing.
-type Clock interface {
- Now() time.Time
-}
-
-// +kubebuilder:docs-gen:collapse=Clock
-
-/*
-Notice that we need a few more RBAC permissions -- since we're creating and
-managing jobs now, we'll need permissions for those, which means adding
-a couple more [markers](/reference/markers/rbac.md).
-*/
-
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
-//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=get
-
-/*
-Now, we get to the heart of the controller -- the reconciler logic.
-*/
-var (
- scheduledTimeAnnotation = "batch.tutorial.kubebuilder.io/scheduled-at"
-)
-
-func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- log := r.Log.WithValues("cronjob", req.NamespacedName)
-
- /*
- ### 1: Load the CronJob by name
-
- We'll fetch the CronJob using our client. All client methods take a
- context (to allow for cancellation) as their first argument, and the object
- in question as their last. Get is a bit special, in that it takes a
- [`NamespacedName`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client?tab=doc#ObjectKey)
- as the middle argument (most don't have a middle argument, as we'll see
- below).
-
- Many client methods also take variadic options at the end.
- */
- var cronJob batch.CronJob
- if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
- log.Error(err, "unable to fetch CronJob")
- // we'll ignore not-found errors, since they can't be fixed by an immediate
- // requeue (we'll need to wait for a new notification), and we can get them
- // on deleted requests.
- return ctrl.Result{}, client.IgnoreNotFound(err)
- }
-
- /*
- ### 2: List all active jobs, and update the status
-
- To fully update our status, we'll need to list all child jobs in this namespace that belong to this CronJob.
- Similarly to Get, we can use the List method to list the child jobs. Notice that we use variadic options to
- set the namespace and field match (which is actually an index lookup that we set up below).
- */
- var childJobs kbatch.JobList
- if err := r.List(ctx, &childJobs, client.InNamespace(req.Namespace), client.MatchingFields{jobOwnerKey: req.Name}); err != nil {
- log.Error(err, "unable to list child Jobs")
- return ctrl.Result{}, err
- }
-
- /*
-
-
-
- What is this index about?
-
- The reconciler fetches all jobs owned by the cronjob for the status. As our number of cronjobs increases,
- looking these up can become quite slow as we have to filter through all of them. For a more efficient lookup,
- these jobs will be indexed locally on the controller's name. A jobOwnerKey field is added to the
- cached job objects. This key references the owning controller and functions as the index. Later in this
- document we will configure the manager to actually index this field.
-
-
-
- Once we have all the jobs we own, we'll split them into active, successful,
- and failed jobs, keeping track of the most recent run so that we can record it
- in status. Remember, status should be able to be reconstituted from the state
- of the world, so it's generally not a good idea to read from the status of the
- root object. Instead, you should reconstruct it every run. That's what we'll
- do here.
-
- We can check if a job is "finished" and whether it succeeded or failed using status
- conditions. We'll put that logic in a helper to make our code cleaner.
- */
-
- // find the active list of jobs
- var activeJobs []*kbatch.Job
- var successfulJobs []*kbatch.Job
- var failedJobs []*kbatch.Job
- var mostRecentTime *time.Time // find the last run so we can update the status
-
- /*
- We consider a job "finished" if it has a "Complete" or "Failed" condition marked as true.
- Status conditions allow us to add extensible status information to our objects that other
- humans and controllers can examine to check things like completion and health.
- */
- isJobFinished := func(job *kbatch.Job) (bool, kbatch.JobConditionType) {
- for _, c := range job.Status.Conditions {
- if (c.Type == kbatch.JobComplete || c.Type == kbatch.JobFailed) && c.Status == corev1.ConditionTrue {
- return true, c.Type
- }
- }
-
- return false, ""
- }
- // +kubebuilder:docs-gen:collapse=isJobFinished
-
- /*
- We'll use a helper to extract the scheduled time from the annotation that
- we added during job creation.
- */
- getScheduledTimeForJob := func(job *kbatch.Job) (*time.Time, error) {
- timeRaw := job.Annotations[scheduledTimeAnnotation]
- if len(timeRaw) == 0 {
- return nil, nil
- }
-
- timeParsed, err := time.Parse(time.RFC3339, timeRaw)
- if err != nil {
- return nil, err
- }
- return &timeParsed, nil
- }
- // +kubebuilder:docs-gen:collapse=getScheduledTimeForJob
-
- for i, job := range childJobs.Items {
- _, finishedType := isJobFinished(&job)
- switch finishedType {
- case "": // ongoing
- activeJobs = append(activeJobs, &childJobs.Items[i])
- case kbatch.JobFailed:
- failedJobs = append(failedJobs, &childJobs.Items[i])
- case kbatch.JobComplete:
- successfulJobs = append(successfulJobs, &childJobs.Items[i])
- }
-
- // We'll store the launch time in an annotation, so we'll reconstitute that from
- // the active jobs themselves.
- scheduledTimeForJob, err := getScheduledTimeForJob(&job)
- if err != nil {
- log.Error(err, "unable to parse schedule time for child job", "job", &job)
- continue
- }
- if scheduledTimeForJob != nil {
- if mostRecentTime == nil {
- mostRecentTime = scheduledTimeForJob
- } else if mostRecentTime.Before(*scheduledTimeForJob) {
- mostRecentTime = scheduledTimeForJob
- }
- }
- }
-
- if mostRecentTime != nil {
- cronJob.Status.LastScheduleTime = &metav1.Time{Time: *mostRecentTime}
- } else {
- cronJob.Status.LastScheduleTime = nil
- }
- cronJob.Status.Active = nil
- for _, activeJob := range activeJobs {
- jobRef, err := ref.GetReference(r.Scheme, activeJob)
- if err != nil {
- log.Error(err, "unable to make reference to active job", "job", activeJob)
- continue
- }
- cronJob.Status.Active = append(cronJob.Status.Active, *jobRef)
- }
-
- /*
- Here, we'll log how many jobs we observed at a slightly higher logging level,
- for debugging. Notice how instead of using a format string, we use a fixed message,
- and attach key-value pairs with the extra information. This makes it easier to
- filter and query log lines.
- */
- log.V(1).Info("job count", "active jobs", len(activeJobs), "successful jobs", len(successfulJobs), "failed jobs", len(failedJobs))
-
- /*
- Using the date we've gathered, we'll update the status of our CRD.
- Just like before, we use our client. To specifically update the status
- subresource, we'll use the `Status` part of the client, with the `Update`
- method.
-
- The status subresource ignores changes to spec, so it's less likely to conflict
- with any other updates, and can have separate permissions.
- */
- if err := r.Status().Update(ctx, &cronJob); err != nil {
- log.Error(err, "unable to update CronJob status")
- return ctrl.Result{}, err
- }
-
- /*
- Once we've updated our status, we can move on to ensuring that the status of
- the world matches what we want in our spec.
-
- ### 3: Clean up old jobs according to the history limit
-
- First, we'll try to clean up old jobs, so that we don't leave too many lying
- around.
- */
-
- // NB: deleting these is "best effort" -- if we fail on a particular one,
- // we won't requeue just to finish the deleting.
- if cronJob.Spec.FailedJobsHistoryLimit != nil {
- sort.Slice(failedJobs, func(i, j int) bool {
- if failedJobs[i].Status.StartTime == nil {
- return failedJobs[j].Status.StartTime != nil
- }
- return failedJobs[i].Status.StartTime.Before(failedJobs[j].Status.StartTime)
- })
- for i, job := range failedJobs {
- if int32(i) >= int32(len(failedJobs))-*cronJob.Spec.FailedJobsHistoryLimit {
- break
- }
- if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
- log.Error(err, "unable to delete old failed job", "job", job)
- } else {
- log.V(0).Info("deleted old failed job", "job", job)
- }
- }
- }
-
- if cronJob.Spec.SuccessfulJobsHistoryLimit != nil {
- sort.Slice(successfulJobs, func(i, j int) bool {
- if successfulJobs[i].Status.StartTime == nil {
- return successfulJobs[j].Status.StartTime != nil
- }
- return successfulJobs[i].Status.StartTime.Before(successfulJobs[j].Status.StartTime)
- })
- for i, job := range successfulJobs {
- if int32(i) >= int32(len(successfulJobs))-*cronJob.Spec.SuccessfulJobsHistoryLimit {
- break
- }
- if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); (err) != nil {
- log.Error(err, "unable to delete old successful job", "job", job)
- } else {
- log.V(0).Info("deleted old successful job", "job", job)
- }
- }
- }
-
- /* ### 4: Check if we're suspended
-
- If this object is suspended, we don't want to run any jobs, so we'll stop now.
- This is useful if something's broken with the job we're running and we want to
- pause runs to investigate or putz with the cluster, without deleting the object.
- */
-
- if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend {
- log.V(1).Info("cronjob suspended, skipping")
- return ctrl.Result{}, nil
- }
-
- /*
- ### 5: Get the next scheduled run
-
- If we're not paused, we'll need to calculate the next scheduled run, and whether
- or not we've got a run that we haven't processed yet.
- */
-
- /*
- We'll calculate the next scheduled time using our helpful cron library.
- We'll start calculating appropriate times from our last run, or the creation
- of the CronJob if we can't find a last run.
-
- If there are too many missed runs and we don't have any deadlines set, we'll
- bail so that we don't cause issues on controller restarts or wedges.
-
- Otherwise, we'll just return the missed runs (of which we'll just use the latest),
- and the next run, so that we can know when it's time to reconcile again.
- */
- getNextSchedule := func(cronJob *batch.CronJob, now time.Time) (lastMissed time.Time, next time.Time, err error) {
- sched, err := cron.ParseStandard(cronJob.Spec.Schedule)
- if err != nil {
- return time.Time{}, time.Time{}, fmt.Errorf("Unparseable schedule %q: %v", cronJob.Spec.Schedule, err)
- }
-
- // for optimization purposes, cheat a bit and start from our last observed run time
- // we could reconstitute this here, but there's not much point, since we've
- // just updated it.
- var earliestTime time.Time
- if cronJob.Status.LastScheduleTime != nil {
- earliestTime = cronJob.Status.LastScheduleTime.Time
- } else {
- earliestTime = cronJob.ObjectMeta.CreationTimestamp.Time
- }
- if cronJob.Spec.StartingDeadlineSeconds != nil {
- // controller is not going to schedule anything below this point
- schedulingDeadline := now.Add(-time.Second * time.Duration(*cronJob.Spec.StartingDeadlineSeconds))
-
- if schedulingDeadline.After(earliestTime) {
- earliestTime = schedulingDeadline
- }
- }
- if earliestTime.After(now) {
- return time.Time{}, sched.Next(now), nil
- }
-
- starts := 0
- for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) {
- lastMissed = t
- // An object might miss several starts. For example, if
- // controller gets wedged on Friday at 5:01pm when everyone has
- // gone home, and someone comes in on Tuesday AM and discovers
- // the problem and restarts the controller, then all the hourly
- // jobs, more than 80 of them for one hourly scheduledJob, should
- // all start running with no further intervention (if the scheduledJob
- // allows concurrency and late starts).
- //
- // However, if there is a bug somewhere, or incorrect clock
- // on controller's server or apiservers (for setting creationTimestamp)
- // then there could be so many missed start times (it could be off
- // by decades or more), that it would eat up all the CPU and memory
- // of this controller. In that case, we want to not try to list
- // all the missed start times.
- starts++
- if starts > 100 {
- // We can't get the most recent times so just return an empty slice
- return time.Time{}, time.Time{}, fmt.Errorf("Too many missed start times (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.")
- }
- }
- return lastMissed, sched.Next(now), nil
- }
- // +kubebuilder:docs-gen:collapse=getNextSchedule
-
- // figure out the next times that we need to create
- // jobs at (or anything we missed).
- missedRun, nextRun, err := getNextSchedule(&cronJob, r.Now())
- if err != nil {
- log.Error(err, "unable to figure out CronJob schedule")
- // we don't really care about requeuing until we get an update that
- // fixes the schedule, so don't return an error
- return ctrl.Result{}, nil
- }
-
- /*
- We'll prep our eventual request to requeue until the next job, and then figure
- out if we actually need to run.
- */
- scheduledResult := ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())} // save this so we can re-use it elsewhere
- log = log.WithValues("now", r.Now(), "next run", nextRun)
-
- /*
- ### 6: Run a new job if it's on schedule, not past the deadline, and not blocked by our concurrency policy
-
- If we've missed a run, and we're still within the deadline to start it, we'll need to run a job.
- */
- if missedRun.IsZero() {
- log.V(1).Info("no upcoming scheduled times, sleeping until next")
- return scheduledResult, nil
- }
-
- // make sure we're not too late to start the run
- log = log.WithValues("current run", missedRun)
- tooLate := false
- if cronJob.Spec.StartingDeadlineSeconds != nil {
- tooLate = missedRun.Add(time.Duration(*cronJob.Spec.StartingDeadlineSeconds) * time.Second).Before(r.Now())
- }
- if tooLate {
- log.V(1).Info("missed starting deadline for last run, sleeping till next")
- // TODO(directxman12): events
- return scheduledResult, nil
- }
-
- /*
- If we actually have to run a job, we'll need to either wait till existing ones finish,
- replace the existing ones, or just add new ones. If our information is out of date due
- to cache delay, we'll get a requeue when we get up-to-date information.
- */
- // figure out how to run this job -- concurrency policy might forbid us from running
- // multiple at the same time...
- if cronJob.Spec.ConcurrencyPolicy == batch.ForbidConcurrent && len(activeJobs) > 0 {
- log.V(1).Info("concurrency policy blocks concurrent runs, skipping", "num active", len(activeJobs))
- return scheduledResult, nil
- }
-
- // ...or instruct us to replace existing ones...
- if cronJob.Spec.ConcurrencyPolicy == batch.ReplaceConcurrent {
- for _, activeJob := range activeJobs {
- // we don't care if the job was already deleted
- if err := r.Delete(ctx, activeJob, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
- log.Error(err, "unable to delete active job", "job", activeJob)
- return ctrl.Result{}, err
- }
- }
- }
-
- /*
- Once we've figured out what to do with existing jobs, we'll actually create our desired job
- */
-
- /*
- We need to construct a job based on our CronJob's template. We'll copy over the spec
- from the template and copy some basic object meta.
-
- Then, we'll set the "scheduled time" annotation so that we can reconstitute our
- `LastScheduleTime` field each reconcile.
-
- Finally, we'll need to set an owner reference. This allows the Kubernetes garbage collector
- to clean up jobs when we delete the CronJob, and allows controller-runtime to figure out
- which cronjob needs to be reconciled when a given job changes (is added, deleted, completes, etc).
- */
- constructJobForCronJob := func(cronJob *batch.CronJob, scheduledTime time.Time) (*kbatch.Job, error) {
- // We want job names for a given nominal start time to have a deterministic name to avoid the same job being created twice
- name := fmt.Sprintf("%s-%d", cronJob.Name, scheduledTime.Unix())
-
- job := &kbatch.Job{
- ObjectMeta: metav1.ObjectMeta{
- Labels: make(map[string]string),
- Annotations: make(map[string]string),
- Name: name,
- Namespace: cronJob.Namespace,
- },
- Spec: *cronJob.Spec.JobTemplate.Spec.DeepCopy(),
- }
- for k, v := range cronJob.Spec.JobTemplate.Annotations {
- job.Annotations[k] = v
- }
- job.Annotations[scheduledTimeAnnotation] = scheduledTime.Format(time.RFC3339)
- for k, v := range cronJob.Spec.JobTemplate.Labels {
- job.Labels[k] = v
- }
- if err := ctrl.SetControllerReference(cronJob, job, r.Scheme); err != nil {
- return nil, err
- }
-
- return job, nil
- }
- // +kubebuilder:docs-gen:collapse=constructJobForCronJob
-
- // actually make the job...
- job, err := constructJobForCronJob(&cronJob, missedRun)
- if err != nil {
- log.Error(err, "unable to construct job from template")
- // don't bother requeuing until we get a change to the spec
- return scheduledResult, nil
- }
-
- // ...and create it on the cluster
- if err := r.Create(ctx, job); err != nil {
- log.Error(err, "unable to create Job for CronJob", "job", job)
- return ctrl.Result{}, err
- }
-
- log.V(1).Info("created Job for CronJob run", "job", job)
-
- /*
- ### 7: Requeue when we either see a running job or it's time for the next scheduled run
-
- Finally, we'll return the result that we prepped above, that says we want to requeue
- when our next run would need to occur. This is taken as a maximum deadline -- if something
- else changes in between, like our job starts or finishes, we get modified, etc, we might
- reconcile again sooner.
- */
- // we'll requeue once we see the running job, and update our status
- return scheduledResult, nil
-}
-
-/*
-### Setup
-
-Finally, we'll update our setup. In order to allow our reconciler to quickly
-look up Jobs by their owner, we'll need an index. We declare an index key that
-we can later use with the client as a pseudo-field name, and then describe how to
-extract the indexed value from the Job object. The indexer will automatically take
-care of namespaces for us, so we just have to extract the owner name if the Job has
-a CronJob owner.
-
-Additionally, we'll inform the manager that this controller owns some Jobs, so that it
-will automatically call Reconcile on the underlying CronJob when a Job changes, is
-deleted, etc.
-*/
-var (
- jobOwnerKey = ".metadata.controller"
- apiGVStr = batch.GroupVersion.String()
-)
-
-func (r *CronJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
- // set up a real clock, since we're not in a test
- if r.Clock == nil {
- r.Clock = realClock{}
- }
-
- if err := mgr.GetFieldIndexer().IndexField(context.Background(), &kbatch.Job{}, jobOwnerKey, func(rawObj client.Object) []string {
- // grab the job object, extract the owner...
- job := rawObj.(*kbatch.Job)
- owner := metav1.GetControllerOf(job)
- if owner == nil {
- return nil
- }
- // ...make sure it's a CronJob...
- if owner.APIVersion != apiGVStr || owner.Kind != "CronJob" {
- return nil
- }
-
- // ...and if so, return it
- return []string{owner.Name}
- }); err != nil {
- return err
- }
-
- return ctrl.NewControllerManagedBy(mgr).
- For(&batch.CronJob{}).
- Owns(&kbatch.Job{}).
- Complete(r)
-}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/controllers/cronjob_controller_test.go b/docs/book/src/cronjob-tutorial/testdata/project/controllers/cronjob_controller_test.go
deleted file mode 100644
index 1498145538b..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/controllers/cronjob_controller_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
-Ideally, we should have one `_conroller_test.go` for each controller scaffolded and called in the `suite_test.go`.
-So, let's write our example test for the CronJob controller (`cronjob_controller_test.go.`)
-*/
-
-/*
-
-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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-/*
-As usual, we start with the necessary imports. We also define some utility variables.
-*/
-package controllers
-
-import (
- "context"
- "reflect"
- "time"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- batchv1 "k8s.io/api/batch/v1"
- batchv1beta1 "k8s.io/api/batch/v1beta1"
- v1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
-
- cronjobv1 "tutorial.kubebuilder.io/project/api/v1"
-)
-
-// +kubebuilder:docs-gen:collapse=Imports
-
-/*
-The first step to writing a simple integration test is to actually create an instance of CronJob you can run tests against.
-Note that to create a CronJob, you’ll need to create a stub CronJob struct that contains your CronJob’s specifications.
-
-Note that when we create a stub CronJob, the CronJob also needs stubs of its required downstream objects.
-Without the stubbed Job template spec and the Pod template spec below, the Kubernetes API will not be able to
-create the CronJob.
-*/
-var _ = Describe("CronJob controller", func() {
-
- // Define utility constants for object names and testing timeouts/durations and intervals.
- const (
- CronjobName = "test-cronjob"
- CronjobNamespace = "default"
- JobName = "test-job"
-
- timeout = time.Second * 10
- duration = time.Second * 10
- interval = time.Millisecond * 250
- )
-
- Context("When updating CronJob Status", func() {
- It("Should increase CronJob Status.Active count when new Jobs are created", func() {
- By("By creating a new CronJob")
- ctx := context.Background()
- cronJob := &cronjobv1.CronJob{
- TypeMeta: metav1.TypeMeta{
- APIVersion: "batch.tutorial.kubebuilder.io/v1",
- Kind: "CronJob",
- },
- ObjectMeta: metav1.ObjectMeta{
- Name: CronjobName,
- Namespace: CronjobNamespace,
- },
- Spec: cronjobv1.CronJobSpec{
- Schedule: "1 * * * *",
- JobTemplate: batchv1beta1.JobTemplateSpec{
- Spec: batchv1.JobSpec{
- // For simplicity, we only fill out the required fields.
- Template: v1.PodTemplateSpec{
- Spec: v1.PodSpec{
- // For simplicity, we only fill out the required fields.
- Containers: []v1.Container{
- {
- Name: "test-container",
- Image: "test-image",
- },
- },
- RestartPolicy: v1.RestartPolicyOnFailure,
- },
- },
- },
- },
- },
- }
- Expect(k8sClient.Create(ctx, cronJob)).Should(Succeed())
-
- /*
- After creating this CronJob, let's check that the CronJob's Spec fields match what we passed in.
- Note that, because the k8s apiserver may not have finished creating a CronJob after our `Create()` call from earlier, we will use Gomega’s Eventually() testing function instead of Expect() to give the apiserver an opportunity to finish creating our CronJob.
-
- `Eventually()` will repeatedly run the function provided as an argument every interval seconds until
- (a) the function’s output matches what’s expected in the subsequent `Should()` call, or
- (b) the number of attempts * interval period exceed the provided timeout value.
-
- In the examples below, timeout and interval are Go Duration values of our choosing.
- */
-
- cronjobLookupKey := types.NamespacedName{Name: CronjobName, Namespace: CronjobNamespace}
- createdCronjob := &cronjobv1.CronJob{}
-
- // We'll need to retry getting this newly created CronJob, given that creation may not immediately happen.
- Eventually(func() bool {
- err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
- if err != nil {
- return false
- }
- return true
- }, timeout, interval).Should(BeTrue())
- // Let's make sure our Schedule string value was properly converted/handled.
- Expect(createdCronjob.Spec.Schedule).Should(Equal("1 * * * *"))
- /*
- Now that we've created a CronJob in our test cluster, the next step is to write a test that actually tests our CronJob controller’s behavior.
- Let’s test the CronJob controller’s logic responsible for updating CronJob.Status.Active with actively running jobs.
- We’ll verify that when a CronJob has a single active downstream Job, its CronJob.Status.Active field contains a reference to this Job.
-
- First, we should get the test CronJob we created earlier, and verify that it currently does not have any active jobs.
- We use Gomega's `Consistently()` check here to ensure that the active job count remains 0 over a duration of time.
- */
- By("By checking the CronJob has zero active Jobs")
- Consistently(func() (int, error) {
- err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
- if err != nil {
- return -1, err
- }
- return len(createdCronjob.Status.Active), nil
- }, duration, interval).Should(Equal(0))
- /*
- Next, we actually create a stubbed Job that will belong to our CronJob, as well as its downstream template specs.
- We set the Job's status's "Active" count to 2 to simulate the Job running two pods, which means the Job is actively running.
-
- We then take the stubbed Job and set its owner reference to point to our test CronJob.
- This ensures that the test Job belongs to, and is tracked by, our test CronJob.
- Once that’s done, we create our new Job instance.
- */
- By("By creating a new Job")
- testJob := &batchv1.Job{
- ObjectMeta: metav1.ObjectMeta{
- Name: JobName,
- Namespace: CronjobNamespace,
- },
- Spec: batchv1.JobSpec{
- Template: v1.PodTemplateSpec{
- Spec: v1.PodSpec{
- // For simplicity, we only fill out the required fields.
- Containers: []v1.Container{
- {
- Name: "test-container",
- Image: "test-image",
- },
- },
- RestartPolicy: v1.RestartPolicyOnFailure,
- },
- },
- },
- Status: batchv1.JobStatus{
- Active: 2,
- },
- }
-
- // Note that your CronJob’s GroupVersionKind is required to set up this owner reference.
- kind := reflect.TypeOf(cronjobv1.CronJob{}).Name()
- gvk := cronjobv1.GroupVersion.WithKind(kind)
-
- controllerRef := metav1.NewControllerRef(createdCronjob, gvk)
- testJob.SetOwnerReferences([]metav1.OwnerReference{*controllerRef})
- Expect(k8sClient.Create(ctx, testJob)).Should(Succeed())
- /*
- Adding this Job to our test CronJob should trigger our controller’s reconciler logic.
- After that, we can write a test that evaluates whether our controller eventually updates our CronJob’s Status field as expected!
- */
- By("By checking that the CronJob has one active Job")
- Eventually(func() ([]string, error) {
- err := k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)
- if err != nil {
- return nil, err
- }
-
- names := []string{}
- for _, job := range createdCronjob.Status.Active {
- names = append(names, job.Name)
- }
- return names, nil
- }, timeout, interval).Should(ConsistOf(JobName), "should list our active job %s in the active jobs list in status", JobName)
- })
- })
-
-})
-
-/*
- After writing all this code, you can run `go test ./...` in your `controllers/` directory again to run your new test!
-*/
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/controllers/suite_test.go b/docs/book/src/cronjob-tutorial/testdata/project/controllers/suite_test.go
deleted file mode 100644
index 60b8a4e4578..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/controllers/suite_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
-When we created the CronJob API with `kubebuilder create api` in a [previous chapter](/cronjob-tutorial/new-api.md), Kubebuilder already did some test work for you.
-Kubebuilder scaffolded a `controllers/suite_test.go` file that does the bare bones of setting up a test environment.
-
-First, it will contain the necessary imports.
-*/
-
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-package controllers
-
-import (
- "path/filepath"
- "testing"
-
- ctrl "sigs.k8s.io/controller-runtime"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- "k8s.io/client-go/kubernetes/scheme"
- "k8s.io/client-go/rest"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-
- 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.
-
-// +kubebuilder:docs-gen:collapse=Imports
-
-/*
-Now, let's go through the code generated.
-*/
-
-var cfg *rest.Config
-var k8sClient client.Client // You'll be using this client in your tests.
-var testEnv *envtest.Environment
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Controller Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- /*
- First, the envtest cluster is configured to read CRDs from the CRD directory Kubebuilder scaffolds for you.
- */
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
- ErrorIfCRDPathMissing: true,
- }
-
- /*
- Then, we start the envtest cluster.
- */
- 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.
- */
- k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
- Expect(err).NotTo(HaveOccurred())
- Expect(k8sClient).NotTo(BeNil())
-
- /*
- One thing that this autogenerated file is missing, however, is a way to actually start your controller.
- The code above will set up a client for interacting with your custom Kind,
- but will not be able to test your controller behavior.
- If you want to test your custom controller logic, you’ll need to add some familiar-looking manager logic
- to your BeforeSuite() function, so you can register your custom controller to run on this test cluster.
-
- You may notice that the code below runs your controller with nearly identical logic to your CronJob project’s main.go!
- The only difference is that the manager is started in a separate goroutine so it does not block the cleanup of envtest
- when you’re done running your tests.
-
- It is not recommended to use the manager client in tests because it is not strongly consistent. Indeed, the manager
- client is designed to do the "right thing" for controllers by default which is to read from caches. The best solution
- is to instantiate a new client using client.New for tests (as k8sClient above). It will provide a client that reads
- directly from the API meaning that you can write tests expecting read-after-write consistency.
-
- However, keep in mind that you should not do this in the controller's conciliation loop (read an object after you have
- written it). Kubernetes favors an approach where you first do some reads, process and then do some writes and return.
- This way, you let the queue take care of the next cycle of readings if they are necessary.
- */
-
- k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
- Scheme: scheme.Scheme,
- })
- Expect(err).ToNot(HaveOccurred())
-
- err = (&CronJobReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Log: ctrl.Log.WithName("controllers").WithName("CronJob"),
- }).SetupWithManager(k8sManager)
- Expect(err).ToNot(HaveOccurred())
-
- go func() {
- err = k8sManager.Start(ctrl.SetupSignalHandler())
- Expect(err).ToNot(HaveOccurred())
- }()
-}, 60)
-
-/*
-Kubebuilder also generates boilerplate functions for cleaning up envtest and actually running your test files in your controllers/ directory.
-You won't need to touch these.
-*/
-
-var _ = AfterSuite(func() {
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
-
-/*
-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!
-*/
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/.helmignore b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/.helmignore
new file mode 100644
index 00000000000..7d92f7fb4f1
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/.helmignore
@@ -0,0 +1,25 @@
+# Patterns to ignore when building Helm packages.
+# Operating system files
+.DS_Store
+
+# Version control directories
+.git/
+.gitignore
+.bzr/
+.hg/
+.hgignore
+.svn/
+
+# Backup and temporary files
+*.swp
+*.tmp
+*.bak
+*.orig
+*~
+
+# IDE and editor-related files
+.idea/
+.vscode/
+
+# Helm chart artifacts
+dist/chart/*.tgz
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/Chart.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/Chart.yaml
new file mode 100644
index 00000000000..4781b2de9dd
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: project
+description: A Helm chart to distribute the project project
+type: application
+version: 0.1.0
+appVersion: "0.1.0"
+icon: "https://example.com/icon.png"
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/_helpers.tpl b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/_helpers.tpl
new file mode 100644
index 00000000000..5aced50daec
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/_helpers.tpl
@@ -0,0 +1,78 @@
+{{/*
+Chart name based on project name.
+Truncated to 63 characters for Kubernetes compatibility.
+*/}}
+{{- define "chart.name" -}}
+{{- if .Chart }}
+ {{- if .Chart.Name }}
+ {{- .Chart.Name | trunc 63 | trimSuffix "-" }}
+ {{- else }}
+ project
+ {{- end }}
+{{- else }}
+ project
+{{- end }}
+{{- end }}
+
+{{/*
+Full name of the chart (with release name prefix).
+Combines release name with chart name.
+Truncated to 63 characters for Kubernetes compatibility.
+*/}}
+{{- define "chart.fullname" -}}
+{{- $name := include "chart.name" . }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+
+{{/*
+Namespace for generated references.
+Always uses the Helm release namespace.
+*/}}
+{{- define "chart.namespaceName" -}}
+{{ .Release.Namespace }}
+{{- end }}
+
+
+
+{{/*
+Service name with proper truncation for Kubernetes 63-character limit.
+Takes a context with .suffix for the service type (e.g., "webhook-service").
+If fullname + suffix exceeds 63 chars, truncates fullname to 45 chars.
+*/}}
+{{- define "chart.serviceName" -}}
+{{- $fullname := include "chart.fullname" .context -}}
+{{- if gt (len $fullname) 45 -}}
+{{- printf "%s-%s" (trunc 45 $fullname | trimSuffix "-") .suffix | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" $fullname .suffix | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end }}
+
+{{/*
+Common labels for Helm charts.
+Includes app version, chart version, app name, instance, and managed-by labels.
+*/}}
+{{- define "chart.labels" -}}
+{{- if .Chart.AppVersion -}}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+{{- if .Chart.Version }}
+helm.sh/chart: {{ .Chart.Version | quote }}
+{{- end }}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels for matching pods and services.
+Only includes name and instance for consistent selection.
+*/}}
+{{- define "chart.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/metrics-certs.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/metrics-certs.yaml
new file mode 100644
index 00000000000..972b8fed25a
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/metrics-certs.yaml
@@ -0,0 +1,18 @@
+{{- if and .Values.certManager.enable .Values.metrics.enable }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-metrics-certs
+ namespace: {{ .Release.Namespace }}
+spec:
+ dnsNames:
+ - {{ include "chart.serviceName" (dict "suffix" "controller-manager-metrics-service" "context" .) }}.{{ .Release.Namespace }}.svc
+ - {{ include "chart.serviceName" (dict "suffix" "controller-manager-metrics-service" "context" .) }}.{{ .Release.Namespace }}.svc.cluster.local
+ issuerRef:
+ kind: Issuer
+ name: {{ include "chart.name" . }}-selfsigned-issuer
+ secretName: metrics-server-cert
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/selfsigned-issuer.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/selfsigned-issuer.yaml
new file mode 100644
index 00000000000..ede8ccde366
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/selfsigned-issuer.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.certManager.enable }}
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-selfsigned-issuer
+ namespace: {{ .Release.Namespace }}
+spec:
+ selfSigned: {}
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/serving-cert.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/serving-cert.yaml
new file mode 100644
index 00000000000..8cc0c296f6b
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/cert-manager/serving-cert.yaml
@@ -0,0 +1,18 @@
+{{- if .Values.certManager.enable }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-serving-cert
+ namespace: {{ .Release.Namespace }}
+spec:
+ dnsNames:
+ - project-webhook-service.{{ .Release.Namespace }}.svc
+ - project-webhook-service.{{ .Release.Namespace }}.svc.cluster.local
+ issuerRef:
+ kind: Issuer
+ name: {{ include "chart.name" . }}-selfsigned-issuer
+ secretName: webhook-server-cert
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/crd/cronjobs.batch.tutorial.kubebuilder.io.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/crd/cronjobs.batch.tutorial.kubebuilder.io.yaml
new file mode 100644
index 00000000000..41393487e9d
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/crd/cronjobs.batch.tutorial.kubebuilder.io.yaml
@@ -0,0 +1,4034 @@
+{{- if .Values.crd.enable }}
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: cronjobs.batch.tutorial.kubebuilder.io
+spec:
+ group: batch.tutorial.kubebuilder.io
+ names:
+ kind: CronJob
+ listKind: CronJobList
+ plural: cronjobs
+ singular: cronjob
+ scope: Namespaced
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ concurrencyPolicy:
+ default: Allow
+ enum:
+ - Allow
+ - Forbid
+ - Replace
+ type: string
+ failedJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ jobTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ backoffLimit:
+ format: int32
+ type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
+ completions:
+ format: int32
+ type: integer
+ managedBy:
+ type: string
+ manualSelector:
+ type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
+ parallelism:
+ format: int32
+ type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
+ template:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ affinity:
+ properties:
+ nodeAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ preference:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ properties:
+ nodeSelectorTerms:
+ items:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podAntiAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ type: object
+ automountServiceAccountToken:
+ type: boolean
+ containers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ dnsConfig:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ searches:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ dnsPolicy:
+ type: string
+ enableServiceLinks:
+ type: boolean
+ ephemeralContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ targetContainerName:
+ type: string
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ hostAliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ ip:
+ type: string
+ required:
+ - ip
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
+ hostIPC:
+ type: boolean
+ hostNetwork:
+ type: boolean
+ hostPID:
+ type: boolean
+ hostUsers:
+ type: boolean
+ hostname:
+ type: string
+ hostnameOverride:
+ type: string
+ imagePullSecrets:
+ items:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ initContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ nodeName:
+ type: string
+ nodeSelector:
+ additionalProperties:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ overhead:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ preemptionPolicy:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ priorityClassName:
+ type: string
+ readinessGates:
+ items:
+ properties:
+ conditionType:
+ type: string
+ required:
+ - conditionType
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ runtimeClassName:
+ type: string
+ schedulerName:
+ type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ securityContext:
+ properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ fsGroup:
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ type: string
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxChangePolicy:
+ type: string
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
+ sysctls:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ type: string
+ serviceAccountName:
+ type: string
+ setHostnameAsFQDN:
+ type: boolean
+ shareProcessNamespace:
+ type: boolean
+ subdomain:
+ type: string
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ tolerations:
+ items:
+ properties:
+ effect:
+ type: string
+ key:
+ type: string
+ operator:
+ type: string
+ tolerationSeconds:
+ format: int64
+ type: integer
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ topologySpreadConstraints:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ format: int32
+ type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
+ topologyKey:
+ type: string
+ whenUnsatisfiable:
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
+ volumes:
+ items:
+ properties:
+ awsElasticBlockStore:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ properties:
+ cachingMode:
+ type: string
+ diskName:
+ type: string
+ diskURI:
+ type: string
+ fsType:
+ default: ext4
+ type: string
+ kind:
+ type: string
+ readOnly:
+ default: false
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ properties:
+ readOnly:
+ type: boolean
+ secretName:
+ type: string
+ shareName:
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ properties:
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ secretFile:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ csi:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ nodePublishSecretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ readOnly:
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ emptyDir:
+ properties:
+ medium:
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ properties:
+ volumeClaimTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ accessModes:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ dataSource:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ storageClassName:
+ type: string
+ volumeAttributesClassName:
+ type: string
+ volumeMode:
+ type: string
+ volumeName:
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ properties:
+ fsType:
+ type: string
+ lun:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ targetWWNs:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ wwids:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ flexVolume:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ type: object
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - driver
+ type: object
+ flocker:
+ properties:
+ datasetName:
+ type: string
+ datasetUUID:
+ type: string
+ type: object
+ gcePersistentDisk:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ pdName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ properties:
+ directory:
+ type: string
+ repository:
+ type: string
+ revision:
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ properties:
+ endpoints:
+ type: string
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ properties:
+ path:
+ type: string
+ type:
+ type: string
+ required:
+ - path
+ type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
+ iscsi:
+ properties:
+ chapAuthDiscovery:
+ type: boolean
+ chapAuthSession:
+ type: boolean
+ fsType:
+ type: string
+ initiatorName:
+ type: string
+ iqn:
+ type: string
+ iscsiInterface:
+ default: default
+ type: string
+ lun:
+ format: int32
+ type: integer
+ portals:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ targetPortal:
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ type: string
+ nfs:
+ properties:
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ server:
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ properties:
+ claimName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ properties:
+ fsType:
+ type: string
+ pdID:
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ sources:
+ items:
+ properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
+ configMap:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ properties:
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
+ type: object
+ secret:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ properties:
+ audience:
+ type: string
+ expirationSeconds:
+ format: int64
+ type: integer
+ path:
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ quobyte:
+ properties:
+ group:
+ type: string
+ readOnly:
+ type: boolean
+ registry:
+ type: string
+ tenant:
+ type: string
+ user:
+ type: string
+ volume:
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ properties:
+ fsType:
+ type: string
+ image:
+ type: string
+ keyring:
+ default: /etc/ceph/keyring
+ type: string
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ pool:
+ default: rbd
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ default: admin
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ properties:
+ fsType:
+ default: xfs
+ type: string
+ gateway:
+ type: string
+ protectionDomain:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ sslEnabled:
+ type: boolean
+ storageMode:
+ default: ThinProvisioned
+ type: string
+ storagePool:
+ type: string
+ system:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ optional:
+ type: boolean
+ secretName:
+ type: string
+ type: object
+ storageos:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeName:
+ type: string
+ volumeNamespace:
+ type: string
+ type: object
+ vsphereVolume:
+ properties:
+ fsType:
+ type: string
+ storagePolicyID:
+ type: string
+ storagePolicyName:
+ type: string
+ volumePath:
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ required:
+ - containers
+ type: object
+ type: object
+ ttlSecondsAfterFinished:
+ format: int32
+ type: integer
+ required:
+ - template
+ type: object
+ type: object
+ schedule:
+ minLength: 0
+ type: string
+ startingDeadlineSeconds:
+ format: int64
+ minimum: 0
+ type: integer
+ successfulJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ suspend:
+ type: boolean
+ required:
+ - jobTemplate
+ - schedule
+ type: object
+ status:
+ properties:
+ active:
+ items:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ resourceVersion:
+ type: string
+ uid:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ lastScheduleTime:
+ format: date-time
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/manager/manager.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/manager/manager.yaml
new file mode 100644
index 00000000000..635890c0934
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/manager/manager.yaml
@@ -0,0 +1,112 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
+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:
+ - args:
+ {{- if .Values.metrics.enable }}
+ - --metrics-bind-address=:{{ .Values.metrics.port }}
+ {{- else }}
+ # Bind to :0 to disable the controller-runtime managed metrics server
+ - --metrics-bind-address=0
+ {{- end }}
+ - --health-probe-bind-address=:8081
+ {{- range .Values.manager.args }}
+ - {{ . }}
+ {{- end }}
+ {{- if and .Values.certManager.enable .Values.metrics.enable }}
+ - --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs
+ {{- end }}
+ {{- if .Values.certManager.enable }}
+ - --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs
+ {{- end }}
+ command:
+ - /manager
+ image: "{{ .Values.manager.image.repository }}:{{ .Values.manager.image.tag }}"
+ imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ ports:
+ - containerPort: {{ .Values.webhook.port }}
+ name: webhook-server
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ {{- if .Values.manager.resources }}
+ {{- toYaml .Values.manager.resources | nindent 20 }}
+ {{- else }}
+ {}
+ {{- end }}
+ securityContext:
+ {{- if .Values.manager.securityContext }}
+ {{- toYaml .Values.manager.securityContext | nindent 20 }}
+ {{- else }}
+ {}
+ {{- end }}
+ volumeMounts:
+ {{- if and .Values.certManager.enable .Values.metrics.enable }}
+ - mountPath: /tmp/k8s-metrics-server/metrics-certs
+ name: metrics-certs
+ readOnly: true
+ {{- end }}
+ {{- if .Values.certManager.enable }}
+ - mountPath: /tmp/k8s-webhook-server/serving-certs
+ name: webhook-certs
+ readOnly: true
+ {{- end }}
+ securityContext:
+ {{- if .Values.manager.podSecurityContext }}
+ {{- toYaml .Values.manager.podSecurityContext | nindent 14 }}
+ {{- else }}
+ {}
+ {{- end }}
+ serviceAccountName: project-controller-manager
+ terminationGracePeriodSeconds: 10
+ volumes:
+ {{- if and .Values.certManager.enable .Values.metrics.enable }}
+ - 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
+ {{- end }}
+ {{- if .Values.certManager.enable }}
+ - name: webhook-certs
+ secret:
+ secretName: webhook-server-cert
+ {{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml
new file mode 100644
index 00000000000..c5b24a9125a
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.metrics.enable }}
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-service
+ namespace: {{ .Release.Namespace }}
+spec:
+ ports:
+ - name: https
+ port: {{ .Values.metrics.port }}
+ protocol: TCP
+ targetPort: {{ .Values.metrics.port }}
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/prometheus/controller-manager-metrics-monitor.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/prometheus/controller-manager-metrics-monitor.yaml
new file mode 100644
index 00000000000..c8e2b22c26e
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/prometheus/controller-manager-metrics-monitor.yaml
@@ -0,0 +1,35 @@
+{{- if .Values.prometheus.enable }}
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-monitor
+ namespace: {{ .Release.Namespace }}
+spec:
+ endpoints:
+ - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+ path: /metrics
+ port: https
+ scheme: https
+ 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
+ serverName: project-controller-manager-metrics-service.{{ .Release.Namespace }}.svc
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/controller-manager.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/controller-manager.yaml
new file mode 100644
index 00000000000..a757441625d
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/controller-manager.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-admin-role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-admin-role.yaml
new file mode 100644
index 00000000000..f0d8d50f4de
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-admin-role.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ 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
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-editor-role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-editor-role.yaml
new file mode 100644
index 00000000000..b9e0db896d6
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-editor-role.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-cronjob-editor-role
+rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-viewer-role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-viewer-role.yaml
new file mode 100644
index 00000000000..88e43a7d001
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-viewer-role.yaml
@@ -0,0 +1,24 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-cronjob-viewer-role
+rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml
new file mode 100644
index 00000000000..17bb2531ff8
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml
@@ -0,0 +1,40 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-leader-election-role
+ namespace: {{ .Release.Namespace }}
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+ - apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml
new file mode 100644
index 00000000000..4966c4ac459
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml
@@ -0,0 +1,16 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-leader-election-rolebinding
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: project-leader-election-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/manager-role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/manager-role.yaml
new file mode 100644
index 00000000000..4261a27fa49
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/manager-role.yaml
@@ -0,0 +1,49 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-manager-role
+rules:
+ - apiGroups:
+ - batch
+ resources:
+ - jobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - batch
+ resources:
+ - jobs/status
+ verbs:
+ - get
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/finalizers
+ verbs:
+ - update
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+ - patch
+ - update
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml
new file mode 100644
index 00000000000..b82536997bc
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-manager-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml
new file mode 100644
index 00000000000..5954a82944e
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml
@@ -0,0 +1,19 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-auth-role
+rules:
+ - apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+ - apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml
new file mode 100644
index 00000000000..33549b4c5ba
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml
@@ -0,0 +1,14 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: project-metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-metrics-auth-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml
new file mode 100644
index 00000000000..609835fe71f
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml
@@ -0,0 +1,11 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-reader
+rules:
+ - nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/mutating-webhook-configuration.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/mutating-webhook-configuration.yaml
new file mode 100644
index 00000000000..c1daf64a335
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/mutating-webhook-configuration.yaml
@@ -0,0 +1,29 @@
+apiVersion: admissionregistration.k8s.io/v1
+kind: MutatingWebhookConfiguration
+metadata:
+ annotations:
+ {{- if .Values.certManager.enable }}
+ cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/project-serving-cert
+ {{- end }}
+ name: project-mutating-webhook-configuration
+webhooks:
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /mutate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/validating-webhook-configuration.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/validating-webhook-configuration.yaml
new file mode 100644
index 00000000000..317669ab731
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/validating-webhook-configuration.yaml
@@ -0,0 +1,29 @@
+apiVersion: admissionregistration.k8s.io/v1
+kind: ValidatingWebhookConfiguration
+metadata:
+ annotations:
+ {{- if .Values.certManager.enable }}
+ cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/project-serving-cert
+ {{- end }}
+ name: project-validating-webhook-configuration
+webhooks:
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /validate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/webhook-service.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/webhook-service.yaml
new file mode 100644
index 00000000000..99213498502
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/templates/webhook/webhook-service.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+spec:
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: {{ .Values.webhook.port }}
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/values.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/values.yaml
new file mode 100644
index 00000000000..e9cb6515c95
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/chart/values.yaml
@@ -0,0 +1,75 @@
+# Configure the controller manager deployment
+manager:
+ replicas: 1
+
+ image:
+ repository: controller
+ tag: latest
+ pullPolicy: IfNotPresent
+
+ # Arguments
+ args:
+ - --leader-elect
+
+ # Environment variables
+ env: []
+
+ # Pod-level security settings
+ podSecurityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+
+ # Container-level security settings
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+
+ # Resource limits and requests
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+
+# Essential RBAC permissions (required for controller operation)
+# These include ServiceAccount, controller permissions, leader election, and metrics access
+# Note: Essential RBAC is always enabled as it's required for the controller to function
+
+# Helper RBAC roles for managing custom resources
+# These provide convenient admin/editor/viewer roles for each CRD type
+# Useful for giving users different levels of access to your custom resources
+rbacHelpers:
+ enable: false # Install convenience admin/editor/viewer roles for CRDs
+
+# Custom Resource Definitions
+crd:
+ enable: true # Install CRDs with the chart
+ keep: true # Keep CRDs when uninstalling
+
+# Controller metrics endpoint.
+# Enable to expose /metrics endpoint with RBAC protection.
+metrics:
+ enable: true
+ port: 8443 # Metrics server port
+
+# Cert-manager integration for TLS certificates.
+# Required for webhook certificates and metrics endpoint certificates.
+certManager:
+ enable: true
+
+# Webhook server configuration
+webhook:
+ enable: true
+ port: 9443 # Webhook server port
+
+# Prometheus ServiceMonitor for metrics scraping.
+# Requires prometheus-operator to be installed in the cluster.
+prometheus:
+ enable: false
+
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml b/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml
new file mode 100644
index 00000000000..9b1155aba1e
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml
@@ -0,0 +1,4549 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-system
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: cronjobs.batch.tutorial.kubebuilder.io
+spec:
+ group: batch.tutorial.kubebuilder.io
+ names:
+ kind: CronJob
+ listKind: CronJobList
+ plural: cronjobs
+ singular: cronjob
+ scope: Namespaced
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ concurrencyPolicy:
+ default: Allow
+ enum:
+ - Allow
+ - Forbid
+ - Replace
+ type: string
+ failedJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ jobTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ backoffLimit:
+ format: int32
+ type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
+ completions:
+ format: int32
+ type: integer
+ managedBy:
+ type: string
+ manualSelector:
+ type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
+ parallelism:
+ format: int32
+ type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
+ template:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ affinity:
+ properties:
+ nodeAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ preference:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ properties:
+ nodeSelectorTerms:
+ items:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podAntiAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ type: object
+ automountServiceAccountToken:
+ type: boolean
+ containers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ dnsConfig:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ searches:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ dnsPolicy:
+ type: string
+ enableServiceLinks:
+ type: boolean
+ ephemeralContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ targetContainerName:
+ type: string
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ hostAliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ ip:
+ type: string
+ required:
+ - ip
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
+ hostIPC:
+ type: boolean
+ hostNetwork:
+ type: boolean
+ hostPID:
+ type: boolean
+ hostUsers:
+ type: boolean
+ hostname:
+ type: string
+ hostnameOverride:
+ type: string
+ imagePullSecrets:
+ items:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ initContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ nodeName:
+ type: string
+ nodeSelector:
+ additionalProperties:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ overhead:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ preemptionPolicy:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ priorityClassName:
+ type: string
+ readinessGates:
+ items:
+ properties:
+ conditionType:
+ type: string
+ required:
+ - conditionType
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ runtimeClassName:
+ type: string
+ schedulerName:
+ type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ securityContext:
+ properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ fsGroup:
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ type: string
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxChangePolicy:
+ type: string
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
+ sysctls:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ type: string
+ serviceAccountName:
+ type: string
+ setHostnameAsFQDN:
+ type: boolean
+ shareProcessNamespace:
+ type: boolean
+ subdomain:
+ type: string
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ tolerations:
+ items:
+ properties:
+ effect:
+ type: string
+ key:
+ type: string
+ operator:
+ type: string
+ tolerationSeconds:
+ format: int64
+ type: integer
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ topologySpreadConstraints:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ format: int32
+ type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
+ topologyKey:
+ type: string
+ whenUnsatisfiable:
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
+ volumes:
+ items:
+ properties:
+ awsElasticBlockStore:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ properties:
+ cachingMode:
+ type: string
+ diskName:
+ type: string
+ diskURI:
+ type: string
+ fsType:
+ default: ext4
+ type: string
+ kind:
+ type: string
+ readOnly:
+ default: false
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ properties:
+ readOnly:
+ type: boolean
+ secretName:
+ type: string
+ shareName:
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ properties:
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ secretFile:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ csi:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ nodePublishSecretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ readOnly:
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ emptyDir:
+ properties:
+ medium:
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ properties:
+ volumeClaimTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ accessModes:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ dataSource:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ storageClassName:
+ type: string
+ volumeAttributesClassName:
+ type: string
+ volumeMode:
+ type: string
+ volumeName:
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ properties:
+ fsType:
+ type: string
+ lun:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ targetWWNs:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ wwids:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ flexVolume:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ type: object
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - driver
+ type: object
+ flocker:
+ properties:
+ datasetName:
+ type: string
+ datasetUUID:
+ type: string
+ type: object
+ gcePersistentDisk:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ pdName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ properties:
+ directory:
+ type: string
+ repository:
+ type: string
+ revision:
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ properties:
+ endpoints:
+ type: string
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ properties:
+ path:
+ type: string
+ type:
+ type: string
+ required:
+ - path
+ type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
+ iscsi:
+ properties:
+ chapAuthDiscovery:
+ type: boolean
+ chapAuthSession:
+ type: boolean
+ fsType:
+ type: string
+ initiatorName:
+ type: string
+ iqn:
+ type: string
+ iscsiInterface:
+ default: default
+ type: string
+ lun:
+ format: int32
+ type: integer
+ portals:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ targetPortal:
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ type: string
+ nfs:
+ properties:
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ server:
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ properties:
+ claimName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ properties:
+ fsType:
+ type: string
+ pdID:
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ sources:
+ items:
+ properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
+ configMap:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ properties:
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
+ type: object
+ secret:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ properties:
+ audience:
+ type: string
+ expirationSeconds:
+ format: int64
+ type: integer
+ path:
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ quobyte:
+ properties:
+ group:
+ type: string
+ readOnly:
+ type: boolean
+ registry:
+ type: string
+ tenant:
+ type: string
+ user:
+ type: string
+ volume:
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ properties:
+ fsType:
+ type: string
+ image:
+ type: string
+ keyring:
+ default: /etc/ceph/keyring
+ type: string
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ pool:
+ default: rbd
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ default: admin
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ properties:
+ fsType:
+ default: xfs
+ type: string
+ gateway:
+ type: string
+ protectionDomain:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ sslEnabled:
+ type: boolean
+ storageMode:
+ default: ThinProvisioned
+ type: string
+ storagePool:
+ type: string
+ system:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ optional:
+ type: boolean
+ secretName:
+ type: string
+ type: object
+ storageos:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeName:
+ type: string
+ volumeNamespace:
+ type: string
+ type: object
+ vsphereVolume:
+ properties:
+ fsType:
+ type: string
+ storagePolicyID:
+ type: string
+ storagePolicyName:
+ type: string
+ volumePath:
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ required:
+ - containers
+ type: object
+ type: object
+ ttlSecondsAfterFinished:
+ format: int32
+ type: integer
+ required:
+ - template
+ type: object
+ type: object
+ schedule:
+ minLength: 0
+ type: string
+ startingDeadlineSeconds:
+ format: int64
+ minimum: 0
+ type: integer
+ successfulJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ suspend:
+ type: boolean
+ required:
+ - jobTemplate
+ - schedule
+ type: object
+ status:
+ properties:
+ active:
+ items:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ resourceVersion:
+ type: string
+ uid:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ lastScheduleTime:
+ format: date-time
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-leader-election-role
+ namespace: project-system
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+---
+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
+ app.kubernetes.io/name: project
+ name: project-cronjob-editor-role
+rules:
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- 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
+ app.kubernetes.io/name: project
+ name: project-cronjob-viewer-role
+rules:
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-manager-role
+rules:
+- apiGroups:
+ - batch
+ resources:
+ - jobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch
+ resources:
+ - jobs/status
+ verbs:
+ - get
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+ - patch
+ - update
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-auth-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-reader
+rules:
+- nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-leader-election-rolebinding
+ namespace: project-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: project-leader-election-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-manager-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: project-metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-metrics-auth-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-service
+ namespace: project-system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: 8443
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-webhook-service
+ namespace: project-system
+spec:
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: 9443
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager
+ namespace: project-system
+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:
+ - args:
+ - --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
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ ports:
+ - containerPort: 9443
+ name: webhook-server
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+ volumeMounts:
+ - mountPath: /tmp/k8s-metrics-server/metrics-certs
+ name: metrics-certs
+ readOnly: true
+ - mountPath: /tmp/k8s-webhook-server/serving-certs
+ name: webhook-certs
+ readOnly: true
+ securityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+ serviceAccountName: project-controller-manager
+ terminationGracePeriodSeconds: 10
+ volumes:
+ - 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:
+ 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:
+ - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+ path: /metrics
+ port: https
+ scheme: https
+ 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
+ serverName: project-controller-manager-metrics-service.project-system.svc
+ 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:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /mutate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+---
+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:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /validate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/go.mod b/docs/book/src/cronjob-tutorial/testdata/project/go.mod
index 50f3f11f644..a3a13944085 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/go.mod
+++ b/docs/book/src/cronjob-tutorial/testdata/project/go.mod
@@ -1,14 +1,101 @@
module tutorial.kubebuilder.io/project
-go 1.15
+go 1.24.6
require (
- github.com/go-logr/logr v0.3.0
- github.com/onsi/ginkgo v1.14.1
- github.com/onsi/gomega v1.10.2
+ github.com/onsi/ginkgo/v2 v2.22.0
+ github.com/onsi/gomega v1.36.1
github.com/robfig/cron v1.2.0
- k8s.io/api v0.19.2
- k8s.io/apimachinery v0.19.2
- k8s.io/client-go v0.19.2
- sigs.k8s.io/controller-runtime v0.7.2
+ k8s.io/api v0.34.1
+ k8s.io/apimachinery v0.34.1
+ k8s.io/client-go v0.34.1
+ k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
+ sigs.k8s.io/controller-runtime v0.22.4
+)
+
+require (
+ cel.dev/expr v0.24.0 // indirect
+ github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/blang/semver/v4 v4.0.0 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+ github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fsnotify/fsnotify v1.9.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/go-logr/zapr v1.3.0 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/google/btree v1.1.3 // indirect
+ github.com/google/cel-go v0.26.0 // indirect
+ github.com/google/gnostic-models v0.7.0 // indirect
+ github.com/google/go-cmp v0.7.0 // indirect
+ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_golang v1.22.0 // indirect
+ github.com/prometheus/client_model v0.6.1 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
+ github.com/spf13/cobra v1.9.1 // indirect
+ github.com/spf13/pflag v1.0.6 // indirect
+ github.com/stoewer/go-strcase v1.3.0 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
+ go.opentelemetry.io/otel v1.35.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.35.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.35.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.0 // indirect
+ go.yaml.in/yaml/v2 v2.4.2 // indirect
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
+ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
+ golang.org/x/net v0.38.0 // indirect
+ golang.org/x/oauth2 v0.27.0 // indirect
+ golang.org/x/sync v0.12.0 // indirect
+ golang.org/x/sys v0.31.0 // indirect
+ golang.org/x/term v0.30.0 // indirect
+ golang.org/x/text v0.23.0 // indirect
+ golang.org/x/time v0.9.0 // indirect
+ golang.org/x/tools v0.26.0 // indirect
+ gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
+ google.golang.org/grpc v1.72.1 // indirect
+ google.golang.org/protobuf v1.36.5 // indirect
+ gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/apiextensions-apiserver v0.34.1 // indirect
+ k8s.io/apiserver v0.34.1 // indirect
+ k8s.io/component-base v0.34.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
+ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
+ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+ sigs.k8s.io/randfill v1.0.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/go.sum b/docs/book/src/cronjob-tutorial/testdata/project/go.sum
index 20c2a3946ea..0c35e595906 100644
--- a/docs/book/src/cronjob-tutorial/testdata/project/go.sum
+++ b/docs/book/src/cronjob-tutorial/testdata/project/go.sum
@@ -1,640 +1,261 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM=
-cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
-github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
-github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
+cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
-github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs=
-github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4=
-github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU=
-github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
-github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
-github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
-github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
-github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
-github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
-github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
-github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
-github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
-github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
-github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
+github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
+github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
-github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
-github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
-github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
-github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
-github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
+github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8=
-go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
-go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
-go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
-go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
+go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
+golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k=
-gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
+gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
+google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
+google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms=
-k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
-k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA=
-k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
-k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
-k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
-k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
-k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc=
-k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
-k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
-k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
-k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
-k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
-k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
-k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
-sigs.k8s.io/controller-runtime v0.7.2 h1:gD2JZp0bBLLuvSRYVNvox+bRCz1UUUxKDjPUCb56Ukk=
-sigs.k8s.io/controller-runtime v0.7.2/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
+k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
+k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
+k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
+k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
+k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
+k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
+k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
+k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
+k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
+sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
+sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
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 225d029d2de..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 2021 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.
@@ -12,5 +12,4 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-*/
-// +kubebuilder:gen-docs:collapse=Apache License
+*/
\ No newline at end of file
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
new file mode 100644
index 00000000000..091bff8a01c
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go
@@ -0,0 +1,718 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+/*
+We'll start out with some imports. You'll see below that we'll need a few more imports
+than those scaffolded for us. We'll talk about each one when we use it.
+*/
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ "sort"
+ "time"
+
+ "github.com/robfig/cron"
+ kbatch "k8s.io/api/batch/v1"
+ corev1 "k8s.io/api/core/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ ref "k8s.io/client-go/tools/reference"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+)
+
+/*
+Next, we'll need a Clock, which will allow us to fake timing in our tests.
+*/
+
+// CronJobReconciler reconciles a CronJob object
+type CronJobReconciler struct {
+ client.Client
+ Scheme *runtime.Scheme
+ Clock
+}
+
+/*
+We'll mock out the clock to make it easier to jump around in time while testing,
+the "real" clock just calls `time.Now`.
+*/
+type realClock struct{}
+
+func (_ realClock) Now() time.Time { return time.Now() } //nolint:staticcheck
+
+// Clock knows how to get the current time.
+// It can be used to fake out timing for testing.
+type Clock interface {
+ Now() time.Time
+}
+
+// +kubebuilder:docs-gen:collapse=Clock Code Implementation
+
+// Definitions to manage status conditions
+const (
+ // typeAvailableCronJob represents the status of the CronJob reconciliation
+ typeAvailableCronJob = "Available"
+ // typeProgressingCronJob represents the status used when the CronJob is being reconciled
+ typeProgressingCronJob = "Progressing"
+ // typeDegradedCronJob represents the status used when the CronJob has encountered an error
+ typeDegradedCronJob = "Degraded"
+)
+
+/*
+Notice that we need a few more RBAC permissions -- since we're creating and
+managing jobs now, we'll need permissions for those, which means adding
+a couple more [markers](/reference/markers/rbac.md).
+*/
+
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
+// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=get
+
+/*
+Now, we get to the heart of the controller -- the reconciler logic.
+*/
+var (
+ scheduledTimeAnnotation = "batch.tutorial.kubebuilder.io/scheduled-at"
+)
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the CronJob object against the actual cluster state, and then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.22.4/pkg/reconcile
+// nolint:gocyclo
+func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ log := logf.FromContext(ctx)
+
+ /*
+ ### 1: Load the CronJob by name
+
+ We'll fetch the CronJob using our client. All client methods take a
+ context (to allow for cancellation) as their first argument, and the object
+ in question as their last. Get is a bit special, in that it takes a
+ [`NamespacedName`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client?tab=doc#ObjectKey)
+ as the middle argument (most don't have a middle argument, as we'll see
+ below).
+
+ Many client methods also take variadic options at the end.
+ */
+ var cronJob batchv1.CronJob
+ if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
+ if apierrors.IsNotFound(err) {
+ // If the custom resource is not found then it usually means that it was deleted or not created
+ // In this way, we will stop the reconciliation
+ log.Info("CronJob resource not found. Ignoring since object must be deleted")
+ return ctrl.Result{}, nil
+ }
+ // Error reading the object - requeue the request.
+ log.Error(err, "Failed to get CronJob")
+ return ctrl.Result{}, err
+ }
+
+ // Initialize status conditions if not yet present
+ if len(cronJob.Status.Conditions) == 0 {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionUnknown,
+ Reason: "Reconciling",
+ Message: "Starting reconciliation",
+ })
+ if err := r.Status().Update(ctx, &cronJob); err != nil {
+ log.Error(err, "Failed to update CronJob status")
+ return ctrl.Result{}, err
+ }
+
+ // Re-fetch the CronJob after updating the status
+ if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
+ log.Error(err, "Failed to re-fetch CronJob")
+ return ctrl.Result{}, err
+ }
+ }
+
+ /*
+ ### 2: List all active jobs, and update the status
+
+ To fully update our status, we'll need to list all child jobs in this namespace that belong to this CronJob.
+ Similarly to Get, we can use the List method to list the child jobs. Notice that we use variadic options to
+ set the namespace and field match (which is actually an index lookup that we set up below).
+ */
+ var childJobs kbatch.JobList
+ if err := r.List(ctx, &childJobs, client.InNamespace(req.Namespace), client.MatchingFields{jobOwnerKey: req.Name}); err != nil {
+ log.Error(err, "unable to list child Jobs")
+ // Update status condition to reflect the error
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "ReconciliationError",
+ Message: fmt.Sprintf("Failed to list child jobs: %v", err),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ return ctrl.Result{}, err
+ }
+
+ /*
+
+
+
+ What is this index about?
+
+ The reconciler fetches all jobs owned by the cronjob for the status. As our number of cronjobs increases,
+ looking these up can become quite slow as we have to filter through all of them. For a more efficient lookup,
+ these jobs will be indexed locally on the controller's name. A jobOwnerKey field is added to the
+ cached job objects. This key references the owning controller and functions as the index. Later in this
+ document we will configure the manager to actually index this field.
+
+
+
+ Once we have all the jobs we own, we'll split them into active, successful,
+ and failed jobs, keeping track of the most recent run so that we can record it
+ in status. Remember, status should be able to be reconstituted from the state
+ of the world, so it's generally not a good idea to read from the status of the
+ root object. Instead, you should reconstruct it every run. That's what we'll
+ do here.
+
+ We can check if a job is "finished" and whether it succeeded or failed using status
+ conditions. We'll put that logic in a helper to make our code cleaner.
+ */
+
+ // find the active list of jobs
+ var activeJobs []*kbatch.Job
+ var successfulJobs []*kbatch.Job
+ var failedJobs []*kbatch.Job
+ var mostRecentTime *time.Time // find the last run so we can update the status
+
+ /*
+ We consider a job "finished" if it has a "Complete" or "Failed" condition marked as true.
+ Status conditions allow us to add extensible status information to our objects that other
+ humans and controllers can examine to check things like completion and health.
+ */
+ isJobFinished := func(job *kbatch.Job) (bool, kbatch.JobConditionType) {
+ for _, c := range job.Status.Conditions {
+ if (c.Type == kbatch.JobComplete || c.Type == kbatch.JobFailed) && c.Status == corev1.ConditionTrue {
+ return true, c.Type
+ }
+ }
+
+ return false, ""
+ }
+ // +kubebuilder:docs-gen:collapse=isJobFinished
+
+ /*
+ We'll use a helper to extract the scheduled time from the annotation that
+ we added during job creation.
+ */
+ getScheduledTimeForJob := func(job *kbatch.Job) (*time.Time, error) {
+ timeRaw := job.Annotations[scheduledTimeAnnotation]
+ if len(timeRaw) == 0 {
+ return nil, nil
+ }
+
+ timeParsed, err := time.Parse(time.RFC3339, timeRaw)
+ if err != nil {
+ return nil, err
+ }
+ return &timeParsed, nil
+ }
+ // +kubebuilder:docs-gen:collapse=getScheduledTimeForJob
+
+ for i, job := range childJobs.Items {
+ _, finishedType := isJobFinished(&job)
+ switch finishedType {
+ case "": // ongoing
+ activeJobs = append(activeJobs, &childJobs.Items[i])
+ case kbatch.JobFailed:
+ failedJobs = append(failedJobs, &childJobs.Items[i])
+ case kbatch.JobComplete:
+ successfulJobs = append(successfulJobs, &childJobs.Items[i])
+ }
+
+ // We'll store the launch time in an annotation, so we'll reconstitute that from
+ // the active jobs themselves.
+ scheduledTimeForJob, err := getScheduledTimeForJob(&job)
+ if err != nil {
+ log.Error(err, "unable to parse schedule time for child job", "job", &job)
+ continue
+ }
+ if scheduledTimeForJob != nil {
+ if mostRecentTime == nil || mostRecentTime.Before(*scheduledTimeForJob) {
+ mostRecentTime = scheduledTimeForJob
+ }
+ }
+ }
+
+ if mostRecentTime != nil {
+ cronJob.Status.LastScheduleTime = &metav1.Time{Time: *mostRecentTime}
+ } else {
+ cronJob.Status.LastScheduleTime = nil
+ }
+ cronJob.Status.Active = nil
+ for _, activeJob := range activeJobs {
+ jobRef, err := ref.GetReference(r.Scheme, activeJob)
+ if err != nil {
+ log.Error(err, "unable to make reference to active job", "job", activeJob)
+ continue
+ }
+ cronJob.Status.Active = append(cronJob.Status.Active, *jobRef)
+ }
+
+ /*
+ Here, we'll log how many jobs we observed at a slightly higher logging level,
+ for debugging. Notice how instead of using a format string, we use a fixed message,
+ and attach key-value pairs with the extra information. This makes it easier to
+ filter and query log lines.
+ */
+ log.V(1).Info("job count", "active jobs", len(activeJobs), "successful jobs", len(successfulJobs), "failed jobs", len(failedJobs))
+
+ // Check if CronJob is suspended
+ isSuspended := cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend
+
+ // Update status conditions based on current state
+ if isSuspended {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionFalse,
+ Reason: "Suspended",
+ Message: "CronJob is suspended",
+ })
+ } else if len(failedJobs) > 0 {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobsFailed",
+ Message: fmt.Sprintf("%d job(s) have failed", len(failedJobs)),
+ })
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionFalse,
+ Reason: "JobsFailed",
+ Message: fmt.Sprintf("%d job(s) have failed", len(failedJobs)),
+ })
+ } else if len(activeJobs) > 0 {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobsActive",
+ Message: fmt.Sprintf("%d job(s) are currently active", len(activeJobs)),
+ })
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobsActive",
+ Message: fmt.Sprintf("CronJob is progressing with %d active job(s)", len(activeJobs)),
+ })
+ } else {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "AllJobsCompleted",
+ Message: "All jobs have completed successfully",
+ })
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionFalse,
+ Reason: "NoJobsActive",
+ Message: "No jobs are currently active",
+ })
+ }
+
+ /*
+ Using the data we've gathered, we'll update the status of our CRD.
+ Just like before, we use our client. To specifically update the status
+ subresource, we'll use the `Status` part of the client, with the `Update`
+ method.
+
+ The status subresource ignores changes to spec, so it's less likely to conflict
+ with any other updates, and can have separate permissions.
+ */
+ if err := r.Status().Update(ctx, &cronJob); err != nil {
+ log.Error(err, "unable to update CronJob status")
+ return ctrl.Result{}, err
+ }
+
+ /*
+ Once we've updated our status, we can move on to ensuring that the status of
+ the world matches what we want in our spec.
+
+ ### 3: Clean up old jobs according to the history limit
+
+ First, we'll try to clean up old jobs, so that we don't leave too many lying
+ around.
+ */
+
+ // NB: deleting these are "best effort" -- if we fail on a particular one,
+ // we won't requeue just to finish the deleting.
+ if cronJob.Spec.FailedJobsHistoryLimit != nil {
+ sort.Slice(failedJobs, func(i, j int) bool {
+ if failedJobs[i].Status.StartTime == nil {
+ return failedJobs[j].Status.StartTime != nil
+ }
+ return failedJobs[i].Status.StartTime.Before(failedJobs[j].Status.StartTime)
+ })
+ for i, job := range failedJobs {
+ if int32(i) >= int32(len(failedJobs))-*cronJob.Spec.FailedJobsHistoryLimit {
+ break
+ }
+ if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
+ log.Error(err, "unable to delete old failed job", "job", job)
+ } else {
+ log.V(0).Info("deleted old failed job", "job", job)
+ }
+ }
+ }
+
+ if cronJob.Spec.SuccessfulJobsHistoryLimit != nil {
+ sort.Slice(successfulJobs, func(i, j int) bool {
+ if successfulJobs[i].Status.StartTime == nil {
+ return successfulJobs[j].Status.StartTime != nil
+ }
+ return successfulJobs[i].Status.StartTime.Before(successfulJobs[j].Status.StartTime)
+ })
+ for i, job := range successfulJobs {
+ if int32(i) >= int32(len(successfulJobs))-*cronJob.Spec.SuccessfulJobsHistoryLimit {
+ break
+ }
+ if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); err != nil {
+ log.Error(err, "unable to delete old successful job", "job", job)
+ } else {
+ log.V(0).Info("deleted old successful job", "job", job)
+ }
+ }
+ }
+
+ /* ### 4: Check if we're suspended
+
+ If this object is suspended, we don't want to run any jobs, so we'll stop now.
+ This is useful if something's broken with the job we're running and we want to
+ pause runs to investigate or putz with the cluster, without deleting the object.
+ */
+
+ if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend {
+ log.V(1).Info("cronjob suspended, skipping")
+ return ctrl.Result{}, nil
+ }
+
+ /*
+ ### 5: Get the next scheduled run
+
+ If we're not paused, we'll need to calculate the next scheduled run, and whether
+ or not we've got a run that we haven't processed yet.
+ */
+
+ /*
+ We'll calculate the next scheduled time using our helpful cron library.
+ We'll start calculating appropriate times from our last run, or the creation
+ of the CronJob if we can't find a last run.
+
+ If there are too many missed runs and we don't have any deadlines set, we'll
+ bail so that we don't cause issues on controller restarts or wedges.
+
+ Otherwise, we'll just return the missed runs (of which we'll just use the latest),
+ and the next run, so that we can know when it's time to reconcile again.
+ */
+ getNextSchedule := func(cronJob *batchv1.CronJob, now time.Time) (lastMissed time.Time, next time.Time, err error) {
+ sched, err := cron.ParseStandard(cronJob.Spec.Schedule)
+ if err != nil {
+ return time.Time{}, time.Time{}, fmt.Errorf("unparseable schedule %q: %w", cronJob.Spec.Schedule, err)
+ }
+
+ // for optimization purposes, cheat a bit and start from our last observed run time
+ // we could reconstitute this here, but there's not much point, since we've
+ // just updated it.
+ var earliestTime time.Time
+ if cronJob.Status.LastScheduleTime != nil {
+ earliestTime = cronJob.Status.LastScheduleTime.Time
+ } else {
+ earliestTime = cronJob.CreationTimestamp.Time
+ }
+ if cronJob.Spec.StartingDeadlineSeconds != nil {
+ // controller is not going to schedule anything below this point
+ schedulingDeadline := now.Add(-time.Second * time.Duration(*cronJob.Spec.StartingDeadlineSeconds))
+
+ if schedulingDeadline.After(earliestTime) {
+ earliestTime = schedulingDeadline
+ }
+ }
+ if earliestTime.After(now) {
+ return time.Time{}, sched.Next(now), nil
+ }
+
+ starts := 0
+ for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) {
+ lastMissed = t
+ // An object might miss several starts. For example, if
+ // controller gets wedged on Friday at 5:01pm when everyone has
+ // gone home, and someone comes in on Tuesday AM and discovers
+ // the problem and restarts the controller, then all the hourly
+ // jobs, more than 80 of them for one hourly scheduledJob, should
+ // all start running with no further intervention (if the scheduledJob
+ // allows concurrency and late starts).
+ //
+ // However, if there is a bug somewhere, or incorrect clock
+ // on controller's server or apiservers (for setting creationTimestamp)
+ // then there could be so many missed start times (it could be off
+ // by decades or more), that it would eat up all the CPU and memory
+ // of this controller. In that case, we want to not try to list
+ // all the missed start times.
+ starts++
+ if starts > 100 {
+ // We can't get the most recent times so just return an empty slice
+ return time.Time{}, time.Time{}, fmt.Errorf("Too many missed start times (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.") //nolint:staticcheck
+ }
+ }
+ return lastMissed, sched.Next(now), nil
+ }
+ // +kubebuilder:docs-gen:collapse=getNextSchedule
+
+ // figure out the next times that we need to create
+ // jobs at (or anything we missed).
+ missedRun, nextRun, err := getNextSchedule(&cronJob, r.Now())
+ if err != nil {
+ log.Error(err, "unable to figure out CronJob schedule")
+ // Update status condition to reflect the schedule error
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "InvalidSchedule",
+ Message: fmt.Sprintf("Failed to parse schedule: %v", err),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ // we don't really care about requeuing until we get an update that
+ // fixes the schedule, so don't return an error
+ return ctrl.Result{}, nil
+ }
+
+ /*
+ We'll prep our eventual request to requeue until the next job, and then figure
+ out if we actually need to run.
+ */
+ scheduledResult := ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())} // save this so we can re-use it elsewhere
+ log = log.WithValues("now", r.Now(), "next run", nextRun)
+
+ /*
+ ### 6: Run a new job if it's on schedule, not past the deadline, and not blocked by our concurrency policy
+
+ If we've missed a run, and we're still within the deadline to start it, we'll need to run a job.
+ */
+ if missedRun.IsZero() {
+ log.V(1).Info("no upcoming scheduled times, sleeping until next")
+ return scheduledResult, nil
+ }
+
+ // make sure we're not too late to start the run
+ log = log.WithValues("current run", missedRun)
+ tooLate := false
+ if cronJob.Spec.StartingDeadlineSeconds != nil {
+ tooLate = missedRun.Add(time.Duration(*cronJob.Spec.StartingDeadlineSeconds) * time.Second).Before(r.Now())
+ }
+ if tooLate {
+ log.V(1).Info("missed starting deadline for last run, sleeping till next")
+ // Update status condition to reflect missed deadline
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "MissedSchedule",
+ Message: fmt.Sprintf("Missed starting deadline for run at %v", missedRun),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ return scheduledResult, nil
+ }
+
+ /*
+ If we actually have to run a job, we'll need to either wait till existing ones finish,
+ replace the existing ones, or just add new ones. If our information is out of date due
+ to cache delay, we'll get a requeue when we get up-to-date information.
+ */
+ // figure out how to run this job -- concurrency policy might forbid us from running
+ // multiple at the same time...
+ if cronJob.Spec.ConcurrencyPolicy == batchv1.ForbidConcurrent && len(activeJobs) > 0 {
+ log.V(1).Info("concurrency policy blocks concurrent runs, skipping", "num active", len(activeJobs))
+ return scheduledResult, nil
+ }
+
+ // ...or instruct us to replace existing ones...
+ if cronJob.Spec.ConcurrencyPolicy == batchv1.ReplaceConcurrent {
+ for _, activeJob := range activeJobs {
+ // we don't care if the job was already deleted
+ if err := r.Delete(ctx, activeJob, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
+ log.Error(err, "unable to delete active job", "job", activeJob)
+ return ctrl.Result{}, err
+ }
+ }
+ }
+
+ /*
+ Once we've figured out what to do with existing jobs, we'll actually create our desired job
+ */
+
+ /*
+ We need to construct a job based on our CronJob's template. We'll copy over the spec
+ from the template and copy some basic object meta.
+
+ Then, we'll set the "scheduled time" annotation so that we can reconstitute our
+ `LastScheduleTime` field each reconcile.
+
+ Finally, we'll need to set an owner reference. This allows the Kubernetes garbage collector
+ to clean up jobs when we delete the CronJob, and allows controller-runtime to figure out
+ which cronjob needs to be reconciled when a given job changes (is added, deleted, completes, etc).
+ */
+ constructJobForCronJob := func(cronJob *batchv1.CronJob, scheduledTime time.Time) (*kbatch.Job, error) {
+ // We want job names for a given nominal start time to have a deterministic name to avoid the same job being created twice
+ name := fmt.Sprintf("%s-%d", cronJob.Name, scheduledTime.Unix())
+
+ job := &kbatch.Job{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: make(map[string]string),
+ Annotations: make(map[string]string),
+ Name: name,
+ Namespace: cronJob.Namespace,
+ },
+ Spec: *cronJob.Spec.JobTemplate.Spec.DeepCopy(),
+ }
+ for k, v := range cronJob.Spec.JobTemplate.Annotations {
+ job.Annotations[k] = v
+ }
+ job.Annotations[scheduledTimeAnnotation] = scheduledTime.Format(time.RFC3339)
+ for k, v := range cronJob.Spec.JobTemplate.Labels {
+ job.Labels[k] = v
+ }
+ if err := ctrl.SetControllerReference(cronJob, job, r.Scheme); err != nil {
+ return nil, err
+ }
+
+ return job, nil
+ }
+ // +kubebuilder:docs-gen:collapse=constructJobForCronJob
+
+ // actually make the job...
+ job, err := constructJobForCronJob(&cronJob, missedRun)
+ if err != nil {
+ log.Error(err, "unable to construct job from template")
+ // don't bother requeuing until we get a change to the spec
+ return scheduledResult, nil
+ }
+
+ // ...and create it on the cluster
+ if err := r.Create(ctx, job); err != nil {
+ log.Error(err, "unable to create Job for CronJob", "job", job)
+ // Update status condition to reflect the error
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobCreationFailed",
+ Message: fmt.Sprintf("Failed to create job: %v", err),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ return ctrl.Result{}, err
+ }
+
+ log.V(1).Info("created Job for CronJob run", "job", job)
+
+ // Update status condition to reflect successful job creation
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobCreated",
+ Message: fmt.Sprintf("Created job %s", job.Name),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+
+ /*
+ ### 7: Requeue when we either see a running job or it's time for the next scheduled run
+
+ Finally, we'll return the result that we prepped above, that says we want to requeue
+ when our next run would need to occur. This is taken as a maximum deadline -- if something
+ else changes in between, like our job starts or finishes, we get modified, etc, we might
+ reconcile again sooner.
+ */
+ // we'll requeue once we see the running job, and update our status
+ return scheduledResult, nil
+}
+
+/*
+### Setup
+
+Finally, we'll update our setup. In order to allow our reconciler to quickly
+look up Jobs by their owner, we'll need an index. We declare an index key that
+we can later use with the client as a pseudo-field name, and then describe how to
+extract the indexed value from the Job object. The indexer will automatically take
+care of namespaces for us, so we just have to extract the owner name if the Job has
+a CronJob owner.
+
+Additionally, we'll inform the manager that this controller owns some Jobs, so that it
+will automatically call Reconcile on the underlying CronJob when a Job changes, is
+deleted, etc.
+*/
+var (
+ jobOwnerKey = ".metadata.controller"
+ apiGVStr = batchv1.GroupVersion.String()
+)
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *CronJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ // set up a real clock, since we're not in a test
+ if r.Clock == nil {
+ r.Clock = realClock{}
+ }
+
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &kbatch.Job{}, jobOwnerKey, func(rawObj client.Object) []string {
+ // grab the job object, extract the owner...
+ job := rawObj.(*kbatch.Job)
+ owner := metav1.GetControllerOf(job)
+ if owner == nil {
+ return nil
+ }
+ // ...make sure it's a CronJob...
+ if owner.APIVersion != apiGVStr || owner.Kind != "CronJob" {
+ return nil
+ }
+
+ // ...and if so, return it
+ return []string{owner.Name}
+ }); err != nil {
+ return err
+ }
+
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&batchv1.CronJob{}).
+ Owns(&kbatch.Job{}).
+ Named("cronjob").
+ Complete(r)
+}
diff --git a/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller_test.go b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller_test.go
new file mode 100644
index 00000000000..a6fbe2d1fe7
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller_test.go
@@ -0,0 +1,215 @@
+/*
+
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+/*
+Ideally, we should have one `_controller_test.go` for each controller scaffolded and called in the `suite_test.go`.
+So, let's write our example test for the CronJob controller (`cronjob_controller_test.go.`)
+*/
+
+/*
+As usual, we start with the necessary imports. We also define some utility variables.
+*/
+package controller
+
+import (
+ "context"
+ "reflect"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ batchv1 "k8s.io/api/batch/v1"
+ v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+
+ cronjobv1 "tutorial.kubebuilder.io/project/api/v1"
+)
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+// Helper function to check if a specific condition exists with expected status
+func hasCondition(conditions []metav1.Condition, conditionType string, expectedStatus metav1.ConditionStatus) bool {
+ for _, condition := range conditions {
+ if condition.Type == conditionType && condition.Status == expectedStatus {
+ return true
+ }
+ }
+ return false
+}
+
+/*
+The first step to writing a simple integration test is to actually create an instance of CronJob you can run tests against.
+Note that to create a CronJob, you’ll need to create a stub CronJob struct that contains your CronJob’s specifications.
+
+Note that when we create a stub CronJob, the CronJob also needs stubs of its required downstream objects.
+Without the stubbed Job template spec and the Pod template spec below, the Kubernetes API will not be able to
+create the CronJob.
+*/
+var _ = Describe("CronJob controller", func() {
+
+ // Define utility constants for object names and testing timeouts/durations and intervals.
+ const (
+ CronjobName = "test-cronjob"
+ CronjobNamespace = "default"
+ JobName = "test-job"
+
+ timeout = time.Second * 10
+ duration = time.Second * 10
+ interval = time.Millisecond * 250
+ )
+
+ Context("When updating CronJob Status", func() {
+ It("Should increase CronJob Status.Active count when new Jobs are created", func() {
+ By("By creating a new CronJob")
+ ctx := context.Background()
+ cronJob := &cronjobv1.CronJob{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "batch.tutorial.kubebuilder.io/v1",
+ Kind: "CronJob",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: CronjobName,
+ Namespace: CronjobNamespace,
+ },
+ Spec: cronjobv1.CronJobSpec{
+ Schedule: "1 * * * *",
+ JobTemplate: batchv1.JobTemplateSpec{
+ Spec: batchv1.JobSpec{
+ // For simplicity, we only fill out the required fields.
+ Template: v1.PodTemplateSpec{
+ Spec: v1.PodSpec{
+ // For simplicity, we only fill out the required fields.
+ Containers: []v1.Container{
+ {
+ Name: "test-container",
+ Image: "test-image",
+ },
+ },
+ RestartPolicy: v1.RestartPolicyOnFailure,
+ },
+ },
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Create(ctx, cronJob)).To(Succeed())
+
+ /*
+ After creating this CronJob, let's check that the CronJob's Spec fields match what we passed in.
+ Note that, because the k8s apiserver may not have finished creating a CronJob after our `Create()` call from earlier, we will use Gomega’s Eventually() testing function instead of Expect() to give the apiserver an opportunity to finish creating our CronJob.
+
+ `Eventually()` will repeatedly run the function provided as an argument every interval seconds until
+ (a) the assertions done by the passed-in `Gomega` succeed, or
+ (b) the number of attempts * interval period exceed the provided timeout value.
+
+ In the examples below, timeout and interval are Go Duration values of our choosing.
+ */
+
+ cronjobLookupKey := types.NamespacedName{Name: CronjobName, Namespace: CronjobNamespace}
+ createdCronjob := &cronjobv1.CronJob{}
+
+ // We'll need to retry getting this newly created CronJob, given that creation may not immediately happen.
+ Eventually(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
+ }, timeout, interval).Should(Succeed())
+ // Let's make sure our Schedule string value was properly converted/handled.
+ Expect(createdCronjob.Spec.Schedule).To(Equal("1 * * * *"))
+ /*
+ Now that we've created a CronJob in our test cluster, the next step is to write a test that actually tests our CronJob controller’s behavior.
+ Let’s test the CronJob controller’s logic responsible for updating CronJob.Status.Active with actively running jobs.
+ We’ll verify that when a CronJob has a single active downstream Job, its CronJob.Status.Active field contains a reference to this Job.
+
+ First, we should get the test CronJob we created earlier, and verify that it currently does not have any active jobs.
+ We use Gomega's `Consistently()` check here to ensure that the active job count remains 0 over a duration of time.
+ */
+ By("By checking the CronJob has zero active Jobs")
+ Consistently(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
+ g.Expect(createdCronjob.Status.Active).To(BeEmpty())
+ }, duration, interval).Should(Succeed())
+ /*
+ Next, we actually create a stubbed Job that will belong to our CronJob, as well as its downstream template specs.
+ We set the Job's status's "Active" count to 2 to simulate the Job running two pods, which means the Job is actively running.
+
+ We then take the stubbed Job and set its owner reference to point to our test CronJob.
+ This ensures that the test Job belongs to, and is tracked by, our test CronJob.
+ Once that’s done, we create our new Job instance.
+ */
+ By("By creating a new Job")
+ testJob := &batchv1.Job{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: JobName,
+ Namespace: CronjobNamespace,
+ },
+ Spec: batchv1.JobSpec{
+ Template: v1.PodTemplateSpec{
+ Spec: v1.PodSpec{
+ // For simplicity, we only fill out the required fields.
+ Containers: []v1.Container{
+ {
+ Name: "test-container",
+ Image: "test-image",
+ },
+ },
+ RestartPolicy: v1.RestartPolicyOnFailure,
+ },
+ },
+ },
+ }
+
+ // Note that your CronJob’s GroupVersionKind is required to set up this owner reference.
+ kind := reflect.TypeOf(cronjobv1.CronJob{}).Name()
+ gvk := cronjobv1.GroupVersion.WithKind(kind)
+
+ controllerRef := metav1.NewControllerRef(createdCronjob, gvk)
+ testJob.SetOwnerReferences([]metav1.OwnerReference{*controllerRef})
+ Expect(k8sClient.Create(ctx, testJob)).To(Succeed())
+ // Note that you can not manage the status values while creating the resource.
+ // The status field is managed separately to reflect the current state of the resource.
+ // Therefore, it should be updated using a PATCH or PUT operation after the resource has been created.
+ // Additionally, it is recommended to use StatusConditions to manage the status. For further information see:
+ // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
+ testJob.Status.Active = 2
+ Expect(k8sClient.Status().Update(ctx, testJob)).To(Succeed())
+ /*
+ Adding this Job to our test CronJob should trigger our controller’s reconciler logic.
+ After that, we can write a test that evaluates whether our controller eventually updates our CronJob’s Status field as expected!
+ */
+ By("By checking that the CronJob has one active Job")
+ Eventually(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed(), "should GET the CronJob")
+ g.Expect(createdCronjob.Status.Active).To(HaveLen(1), "should have exactly one active job")
+ g.Expect(createdCronjob.Status.Active[0].Name).To(Equal(JobName), "the wrong job is active")
+ }, timeout, interval).Should(Succeed(), "should list our active job %s in the active jobs list in status", JobName)
+
+ By("By checking that the CronJob status conditions are properly set")
+ Eventually(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
+ // Check that the Available condition is set to True when job is active
+ g.Expect(hasCondition(createdCronjob.Status.Conditions, "Available", metav1.ConditionTrue)).To(BeTrue(),
+ "CronJob should have Available condition set to True")
+ }, timeout, interval).Should(Succeed())
+ })
+ })
+
+})
+
+// +kubebuilder:docs-gen:collapse=Remaining code from cronjob_controller_test.go
+
+/*
+ After writing all this code, you can run `go test ./...` in your `controllers/` directory again to run your new test!
+*/
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
new file mode 100644
index 00000000000..c74dd849aac
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/controller/suite_test.go
@@ -0,0 +1,200 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+/*
+When we created the CronJob API with `kubebuilder create api` in a [previous chapter](/cronjob-tutorial/new-api.md), Kubebuilder already did some test work for you.
+Kubebuilder scaffolded a `internal/controller/suite_test.go` file that does the bare bones of setting up a test environment.
+
+First, it will contain the necessary imports.
+*/
+
+package controller
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+ "testing"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+ 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.
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+/*
+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.
+)
+
+func TestControllers(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Controller Suite")
+}
+
+var _ = BeforeSuite(func() {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+
+ 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
+
+ /*
+ 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,
+ }
+
+ // Retrieve the first found binary directory to allow running tests from IDEs
+ if getFirstFoundEnvTestBinaryDir() != "" {
+ testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
+ }
+ /*
+ Then, we start the envtest cluster.
+ */
+
+ // cfg is defined in this file globally.
+ cfg, err = testEnv.Start()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(cfg).NotTo(BeNil())
+
+ /*
+ A client is created for our test CRUD operations.
+ */
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ /*
+ One thing that this autogenerated file is missing, however, is a way to actually start your controller.
+ The code above will set up a client for interacting with your custom Kind,
+ but will not be able to test your controller behavior.
+ If you want to test your custom controller logic, you’ll need to add some familiar-looking manager logic
+ to your BeforeSuite() function, so you can register your custom controller to run on this test cluster.
+
+ You may notice that the code below runs your controller with nearly identical logic to your CronJob project’s main.go!
+ The only difference is that the manager is started in a separate goroutine so it does not block the cleanup of envtest
+ when you’re done running your tests.
+
+ Note that we set up both a "live" k8s client and a separate client from the manager. This is because when making
+ assertions in tests, you generally want to assert against the live state of the API server. If you use the client
+ from the manager (`k8sManager.GetClient`), you'd end up asserting against the contents of the cache instead, which is
+ slower and can introduce flakiness into your tests. We could use the manager's `APIReader` to accomplish the same
+ thing, but that would leave us with two clients in our test assertions and setup (one for reading, one for writing),
+ and it'd be easy to make mistakes.
+
+ Note that we keep the reconciler running against the manager's cache client, though -- we want our controller to
+ behave as it would in production, and we use features of the cache (like indices) in our controller which aren't
+ available when talking directly to the API server.
+ */
+ k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&CronJobReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ go func() {
+ defer GinkgoRecover()
+ err = k8sManager.Start(ctx)
+ Expect(err).ToNot(HaveOccurred(), "failed to run manager")
+ }()
+})
+
+/*
+Kubebuilder also generates boilerplate functions for cleaning up envtest and actually running your test files in your controllers/ directory.
+You won't need to touch these.
+*/
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ cancel()
+ err := testEnv.Stop()
+ Expect(err).NotTo(HaveOccurred())
+})
+
+// +kubebuilder:docs-gen:collapse=Remaining code from suite_test.go
+
+/*
+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
new file mode 100644
index 00000000000..a11fb95e8a3
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go
@@ -0,0 +1,282 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+package v1
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/robfig/cron"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ validationutils "k8s.io/apimachinery/pkg/util/validation"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+)
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+/*
+Next, we'll setup a logger for the webhooks.
+*/
+
+var cronjoblog = logf.Log.WithName("cronjob-resource")
+
+/*
+Then, we set up the webhook with the manager.
+*/
+
+// SetupCronJobWebhookWithManager registers the webhook for CronJob in the manager.
+func SetupCronJobWebhookWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).For(&batchv1.CronJob{}).
+ WithValidator(&CronJobCustomValidator{}).
+ WithDefaulter(&CronJobCustomDefaulter{
+ DefaultConcurrencyPolicy: batchv1.AllowConcurrent,
+ DefaultSuspend: false,
+ DefaultSuccessfulJobsHistoryLimit: 3,
+ DefaultFailedJobsHistoryLimit: 1,
+ }).
+ Complete()
+}
+
+/*
+Notice that we use kubebuilder markers to generate webhook manifests.
+This marker is responsible for generating a mutating webhook manifest.
+
+The meaning of each marker can be found [here](/reference/markers/webhook.md).
+*/
+
+/*
+This marker is responsible for generating a mutation webhook manifest.
+*/
+
+// +kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob-v1.kb.io,admissionReviewVersions=v1
+
+// CronJobCustomDefaulter struct is responsible for setting default values on the custom resource of the
+// Kind CronJob when those are created or updated.
+//
+// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
+// as it is used only for temporary operations and does not need to be deeply copied.
+type CronJobCustomDefaulter struct {
+
+ // Default values for various CronJob fields
+ DefaultConcurrencyPolicy batchv1.ConcurrencyPolicy
+ DefaultSuspend bool
+ DefaultSuccessfulJobsHistoryLimit int32
+ DefaultFailedJobsHistoryLimit int32
+}
+
+var _ webhook.CustomDefaulter = &CronJobCustomDefaulter{}
+
+/*
+We use the `webhook.CustomDefaulter`interface to set defaults to our CRD.
+A webhook will automatically be served that calls this defaulting.
+
+The `Default`method is expected to mutate the receiver, setting the defaults.
+*/
+
+// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind CronJob.
+func (d *CronJobCustomDefaulter) Default(_ context.Context, obj runtime.Object) error {
+ cronjob, ok := obj.(*batchv1.CronJob)
+
+ if !ok {
+ return fmt.Errorf("expected an CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Defaulting for CronJob", "name", cronjob.GetName())
+
+ // Set default values
+ d.applyDefaults(cronjob)
+ return nil
+}
+
+// applyDefaults applies default values to CronJob fields.
+func (d *CronJobCustomDefaulter) applyDefaults(cronJob *batchv1.CronJob) {
+ if cronJob.Spec.ConcurrencyPolicy == "" {
+ cronJob.Spec.ConcurrencyPolicy = d.DefaultConcurrencyPolicy
+ }
+ if cronJob.Spec.Suspend == nil {
+ cronJob.Spec.Suspend = new(bool)
+ *cronJob.Spec.Suspend = d.DefaultSuspend
+ }
+ if cronJob.Spec.SuccessfulJobsHistoryLimit == nil {
+ cronJob.Spec.SuccessfulJobsHistoryLimit = new(int32)
+ *cronJob.Spec.SuccessfulJobsHistoryLimit = d.DefaultSuccessfulJobsHistoryLimit
+ }
+ if cronJob.Spec.FailedJobsHistoryLimit == nil {
+ cronJob.Spec.FailedJobsHistoryLimit = new(int32)
+ *cronJob.Spec.FailedJobsHistoryLimit = d.DefaultFailedJobsHistoryLimit
+ }
+}
+
+/*
+We can validate our CRD beyond what's possible with declarative
+validation. Generally, declarative validation should be sufficient, but
+sometimes more advanced use cases call for complex validation.
+
+For instance, we'll see below that we use this to validate a well-formed cron
+schedule without making up a long regular expression.
+
+If `webhook.CustomValidator` interface is implemented, a webhook will automatically be
+served that calls the validation.
+
+The `ValidateCreate`, `ValidateUpdate` and `ValidateDelete` methods are expected
+to validate its receiver upon creation, update and deletion respectively.
+We separate out ValidateCreate from ValidateUpdate to allow behavior like making
+certain fields immutable, so that they can only be set on creation.
+ValidateDelete is also separated from ValidateUpdate to allow different
+validation behavior on deletion.
+Here, however, we just use the same shared validation for `ValidateCreate` and
+`ValidateUpdate`. And we do nothing in `ValidateDelete`, since we don't need to
+validate anything on deletion.
+*/
+
+/*
+This marker is responsible for generating a validation webhook manifest.
+*/
+
+// NOTE: If you want to customise the 'path', use the flags '--defaulting-path' or '--validation-path'.
+// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=vcronjob-v1.kb.io,admissionReviewVersions=v1
+
+// CronJobCustomValidator struct is responsible for validating the CronJob resource
+// when it is created, updated, or deleted.
+//
+// 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
+}
+
+var _ webhook.CustomValidator = &CronJobCustomValidator{}
+
+// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := obj.(*batchv1.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Validation for CronJob upon creation", "name", cronjob.GetName())
+
+ return nil, validateCronJob(cronjob)
+}
+
+// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := newObj.(*batchv1.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object for the newObj but got %T", newObj)
+ }
+ cronjoblog.Info("Validation for CronJob upon update", "name", cronjob.GetName())
+
+ return nil, validateCronJob(cronjob)
+}
+
+// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := obj.(*batchv1.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Validation for CronJob upon deletion", "name", cronjob.GetName())
+
+ // TODO(user): fill in your validation logic upon object deletion.
+
+ return nil, nil
+}
+
+/*
+We validate the name and the spec of the CronJob.
+*/
+
+// validateCronJob validates the fields of a CronJob object.
+func validateCronJob(cronjob *batchv1.CronJob) error {
+ var allErrs field.ErrorList
+ if err := validateCronJobName(cronjob); err != nil {
+ allErrs = append(allErrs, err)
+ }
+ if err := validateCronJobSpec(cronjob); err != nil {
+ allErrs = append(allErrs, err)
+ }
+ if len(allErrs) == 0 {
+ return nil
+ }
+
+ return apierrors.NewInvalid(
+ schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"},
+ cronjob.Name, allErrs)
+}
+
+/*
+Some fields are declaratively validated by OpenAPI schema.
+You can find kubebuilder validation markers (prefixed
+with `// +kubebuilder:validation`) in the
+[Designing an API](api-design.md) section.
+You can find all of the kubebuilder supported markers for
+declaring validation by running `controller-gen crd -w`,
+or [here](/reference/markers/crd-validation.md).
+*/
+
+func validateCronJobSpec(cronjob *batchv1.CronJob) *field.Error {
+ // The field helpers from the kubernetes API machinery help us return nicely
+ // structured validation errors.
+ return validateScheduleFormat(
+ cronjob.Spec.Schedule,
+ field.NewPath("spec").Child("schedule"))
+}
+
+/*
+We'll need to validate the [cron](https://en.wikipedia.org/wiki/Cron) schedule
+is well-formatted.
+*/
+
+func validateScheduleFormat(schedule string, fldPath *field.Path) *field.Error {
+ if _, err := cron.ParseStandard(schedule); err != nil {
+ return field.Invalid(fldPath, schedule, err.Error())
+ }
+ return nil
+}
+
+/*
+Validating the length of a string field can be done declaratively by
+the validation schema.
+
+But the `ObjectMeta.Name` field is defined in a shared package under
+the apimachinery repo, so we can't declaratively validate it using
+the validation schema.
+*/
+
+func validateCronJobName(cronjob *batchv1.CronJob) *field.Error {
+ if len(cronjob.Name) > validationutils.DNS1035LabelMaxLength-11 {
+ // The job name length is 63 characters like all Kubernetes objects
+ // (which must fit in a DNS subdomain). The cronjob controller appends
+ // a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
+ // a job. The job name length limit is 63 characters. Therefore cronjob
+ // names must have length <= 63-11=52. If we don't validate this here,
+ // then job creation will fail later.
+ return field.Invalid(field.NewPath("metadata").Child("name"), cronjob.Name, "must be no more than 52 characters")
+ }
+ return nil
+}
+
+// +kubebuilder:docs-gen:collapse=validateCronJobName() Code Implementation
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
new file mode 100644
index 00000000000..a52e890b74a
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go
@@ -0,0 +1,171 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1
+
+import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+ // TODO (user): Add any additional imports if needed
+ "k8s.io/utils/ptr"
+)
+
+var _ = Describe("CronJob Webhook", func() {
+ var (
+ obj *batchv1.CronJob
+ oldObj *batchv1.CronJob
+ validator CronJobCustomValidator
+ defaulter CronJobCustomDefaulter
+ )
+
+ const validCronJobName = "valid-cronjob-name"
+ const schedule = "*/5 * * * *"
+
+ BeforeEach(func() {
+ obj = &batchv1.CronJob{
+ Spec: batchv1.CronJobSpec{
+ Schedule: schedule,
+ ConcurrencyPolicy: batchv1.AllowConcurrent,
+ SuccessfulJobsHistoryLimit: ptr.To(int32(3)),
+ FailedJobsHistoryLimit: ptr.To(int32(1)),
+ },
+ }
+ *obj.Spec.SuccessfulJobsHistoryLimit = 3
+ *obj.Spec.FailedJobsHistoryLimit = 1
+
+ oldObj = &batchv1.CronJob{
+ Spec: batchv1.CronJobSpec{
+ Schedule: schedule,
+ ConcurrencyPolicy: batchv1.AllowConcurrent,
+ SuccessfulJobsHistoryLimit: ptr.To(int32(3)),
+ FailedJobsHistoryLimit: ptr.To(int32(1)),
+ },
+ }
+ *oldObj.Spec.SuccessfulJobsHistoryLimit = 3
+ *oldObj.Spec.FailedJobsHistoryLimit = 1
+
+ validator = CronJobCustomValidator{}
+ defaulter = CronJobCustomDefaulter{
+ DefaultConcurrencyPolicy: batchv1.AllowConcurrent,
+ DefaultSuspend: false,
+ DefaultSuccessfulJobsHistoryLimit: 3,
+ DefaultFailedJobsHistoryLimit: 1,
+ }
+
+ Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
+ Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
+ })
+
+ AfterEach(func() {
+ // TODO (user): Add any teardown logic common to all tests
+ })
+
+ Context("When creating CronJob under Defaulting Webhook", func() {
+ It("Should apply defaults when a required field is empty", func() {
+ By("simulating a scenario where defaults should be applied")
+ obj.Spec.ConcurrencyPolicy = "" // This should default to AllowConcurrent
+ obj.Spec.Suspend = nil // This should default to false
+ obj.Spec.SuccessfulJobsHistoryLimit = nil // This should default to 3
+ obj.Spec.FailedJobsHistoryLimit = nil // This should default to 1
+
+ By("calling the Default method to apply defaults")
+ _ = defaulter.Default(ctx, obj)
+
+ By("checking that the default values are set")
+ Expect(obj.Spec.ConcurrencyPolicy).To(Equal(batchv1.AllowConcurrent), "Expected ConcurrencyPolicy to default to AllowConcurrent")
+ Expect(*obj.Spec.Suspend).To(BeFalse(), "Expected Suspend to default to false")
+ Expect(*obj.Spec.SuccessfulJobsHistoryLimit).To(Equal(int32(3)), "Expected SuccessfulJobsHistoryLimit to default to 3")
+ Expect(*obj.Spec.FailedJobsHistoryLimit).To(Equal(int32(1)), "Expected FailedJobsHistoryLimit to default to 1")
+ })
+
+ It("Should not overwrite fields that are already set", func() {
+ By("setting fields that would normally get a default")
+ obj.Spec.ConcurrencyPolicy = batchv1.ForbidConcurrent
+ obj.Spec.Suspend = ptr.To(true)
+ obj.Spec.SuccessfulJobsHistoryLimit = ptr.To(int32(5))
+ obj.Spec.FailedJobsHistoryLimit = ptr.To(int32(2))
+
+ By("calling the Default method to apply defaults")
+ _ = defaulter.Default(ctx, obj)
+
+ By("checking that the fields were not overwritten")
+ Expect(obj.Spec.ConcurrencyPolicy).To(Equal(batchv1.ForbidConcurrent), "Expected ConcurrencyPolicy to retain its set value")
+ Expect(obj.Spec.Suspend).NotTo(BeNil())
+ Expect(*obj.Spec.Suspend).To(BeTrue(), "Expected Suspend to retain its set value")
+ Expect(obj.Spec.SuccessfulJobsHistoryLimit).NotTo(BeNil())
+ Expect(*obj.Spec.SuccessfulJobsHistoryLimit).To(Equal(int32(5)), "Expected SuccessfulJobsHistoryLimit to retain its set value")
+ Expect(obj.Spec.FailedJobsHistoryLimit).NotTo(BeNil())
+ Expect(*obj.Spec.FailedJobsHistoryLimit).To(Equal(int32(2)), "Expected FailedJobsHistoryLimit to retain its set value")
+ })
+ })
+
+ Context("When creating or updating CronJob under Validating Webhook", func() {
+ It("Should deny creation if the name is too long", func() {
+ obj.Name = "this-name-is-way-too-long-and-should-fail-validation-because-it-is-way-too-long"
+ Expect(validator.ValidateCreate(ctx, obj)).Error().To(
+ MatchError(ContainSubstring("must be no more than 52 characters")),
+ "Expected name validation to fail for a too-long name")
+ })
+
+ It("Should admit creation if the name is valid", func() {
+ obj.Name = validCronJobName
+ Expect(validator.ValidateCreate(ctx, obj)).To(BeNil(),
+ "Expected name validation to pass for a valid name")
+ })
+
+ It("Should deny creation if the schedule is invalid", func() {
+ obj.Spec.Schedule = "invalid-cron-schedule"
+ Expect(validator.ValidateCreate(ctx, obj)).Error().To(
+ MatchError(ContainSubstring("Expected exactly 5 fields, found 1: invalid-cron-schedule")),
+ "Expected spec validation to fail for an invalid schedule")
+ })
+
+ It("Should admit creation if the schedule is valid", func() {
+ obj.Spec.Schedule = schedule
+ Expect(validator.ValidateCreate(ctx, obj)).To(BeNil(),
+ "Expected spec validation to pass for a valid schedule")
+ })
+
+ It("Should deny update if both name and spec are invalid", func() {
+ oldObj.Name = validCronJobName
+ oldObj.Spec.Schedule = schedule
+
+ By("simulating an update")
+ obj.Name = "this-name-is-way-too-long-and-should-fail-validation-because-it-is-way-too-long"
+ obj.Spec.Schedule = "invalid-cron-schedule"
+
+ By("validating an update")
+ Expect(validator.ValidateUpdate(ctx, oldObj, obj)).Error().To(HaveOccurred(),
+ "Expected validation to fail for both name and spec")
+ })
+
+ It("Should admit update if both name and spec are valid", func() {
+ oldObj.Name = validCronJobName
+ oldObj.Spec.Schedule = schedule
+
+ By("simulating an update")
+ obj.Name = "valid-cronjob-name-updated"
+ obj.Spec.Schedule = "0 0 * * *"
+
+ By("validating an update")
+ Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil(),
+ "Expected validation to pass for a valid update")
+ })
+ })
+
+})
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
new file mode 100644
index 00000000000..e286589ed57
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go
@@ -0,0 +1,164 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "net"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "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 (
+ ctx context.Context
+ cancel context.CancelFunc
+ k8sClient client.Client
+ cfg *rest.Config
+ testEnv *envtest.Environment
+)
+
+func TestAPIs(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Webhook Suite")
+}
+
+var _ = BeforeSuite(func() {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+
+ ctx, cancel = context.WithCancel(context.TODO())
+
+ var err error
+ err = batchv1.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: false,
+
+ WebhookInstallOptions: envtest.WebhookInstallOptions{
+ Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
+ },
+ }
+
+ // 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())
+
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ // start webhook server using Manager.
+ webhookInstallOptions := &testEnv.WebhookInstallOptions
+ mgr, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ WebhookServer: webhook.NewServer(webhook.Options{
+ Host: webhookInstallOptions.LocalServingHost,
+ Port: webhookInstallOptions.LocalServingPort,
+ CertDir: webhookInstallOptions.LocalServingCertDir,
+ }),
+ LeaderElection: false,
+ Metrics: metricsserver.Options{BindAddress: "0"},
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ err = SetupCronJobWebhookWithManager(mgr)
+ Expect(err).NotTo(HaveOccurred())
+
+ // +kubebuilder:scaffold:webhook
+
+ go func() {
+ defer GinkgoRecover()
+ err = mgr.Start(ctx)
+ Expect(err).NotTo(HaveOccurred())
+ }()
+
+ // wait for the webhook server to get ready.
+ dialer := &net.Dialer{Timeout: time.Second}
+ addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
+ Eventually(func() error {
+ conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
+ if err != nil {
+ return err
+ }
+
+ return conn.Close()
+ }).Should(Succeed())
+})
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ cancel()
+ 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/main.go b/docs/book/src/cronjob-tutorial/testdata/project/main.go
deleted file mode 100644
index 010728447a4..00000000000
--- a/docs/book/src/cronjob-tutorial/testdata/project/main.go
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-package main
-
-import (
- "flag"
- "os"
-
- // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
- // to ensure that exec-entrypoint and run can make use of them.
- _ "k8s.io/client-go/plugin/pkg/client/auth"
-
- "k8s.io/apimachinery/pkg/runtime"
- 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/healthz"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-
- batchv1 "tutorial.kubebuilder.io/project/api/v1"
- "tutorial.kubebuilder.io/project/controllers"
- //+kubebuilder:scaffold:imports
-)
-
-// +kubebuilder:docs-gen:collapse=Imports
-
-/*
-The first difference to notice is that kubebuilder has added the new API
-group's package (`batchv1`) to our scheme. This means that we can use those
-objects in our controller.
-
-If we would be using any other CRD we would have to add their scheme the same way.
-Builtin types such as Job have their scheme added by `clientgoscheme`.
-*/
-var (
- scheme = runtime.NewScheme()
- setupLog = ctrl.Log.WithName("setup")
-)
-
-func init() {
- utilruntime.Must(clientgoscheme.AddToScheme(scheme))
-
- utilruntime.Must(batchv1.AddToScheme(scheme))
- //+kubebuilder:scaffold:scheme
-}
-
-/*
-The other thing that's changed is that kubebuilder has added a block calling our
-CronJob controller's `SetupWithManager` method.
-*/
-
-func main() {
- /*
- */
- var metricsAddr string
- var enableLeaderElection bool
- var probeAddr string
- flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
- flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
- flag.BoolVar(&enableLeaderElection, "leader-elect", false,
- "Enable leader election for controller manager. "+
- "Enabling this will ensure there is only one active controller manager.")
-
- opts := zap.Options{
- Development: true,
- }
- opts.BindFlags(flag.CommandLine)
- flag.Parse()
-
- ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
-
- mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- MetricsBindAddress: metricsAddr,
- Port: 9443,
- HealthProbeBindAddress: probeAddr,
- LeaderElection: enableLeaderElection,
- LeaderElectionID: "80807133.tutorial.kubebuilder.io",
- })
- if err != nil {
- setupLog.Error(err, "unable to start manager")
- os.Exit(1)
- }
-
- // +kubebuilder:docs-gen:collapse=old stuff
-
- if err = (&controllers.CronJobReconciler{
- Client: mgr.GetClient(),
- Log: ctrl.Log.WithName("controllers").WithName("CronJob"),
- Scheme: mgr.GetScheme(),
- }).SetupWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create controller", "controller", "CronJob")
- os.Exit(1)
- }
-
- /*
- We'll also set up webhooks for our type, which we'll talk about next.
- We just need to add them to the manager. Since we might want to run
- the webhooks separately, or not run them when testing our controller
- locally, we'll put them behind an environment variable.
-
- We'll just make sure to set `ENABLE_WEBHOOKS=false` when we run locally.
- */
- if os.Getenv("ENABLE_WEBHOOKS") != "false" {
- if err = (&batchv1.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
- os.Exit(1)
- }
- }
- //+kubebuilder:scaffold:builder
-
- if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
- setupLog.Error(err, "unable to set up health check")
- os.Exit(1)
- }
- if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
- setupLog.Error(err, "unable to set up ready check")
- os.Exit(1)
- }
-
- setupLog.Info("starting manager")
- if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
- setupLog.Error(err, "problem running manager")
- os.Exit(1)
- }
- // +kubebuilder:docs-gen:collapse=old stuff
-}
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
new file mode 100644
index 00000000000..c33c3bb67ef
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_suite_test.go
@@ -0,0 +1,115 @@
+//go:build e2e
+// +build e2e
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "tutorial.kubebuilder.io/project/test/utils"
+)
+
+var (
+ // Optional Environment Variables:
+ // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup.
+ // These variables are useful if CertManager is already installed, avoiding
+ // re-installation and conflicts.
+ skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true"
+ // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster
+ isCertManagerAlreadyInstalled = false
+ // isPrometheusOperatorAlreadyInstalled will be set true when prometheus CRDs be found on the cluster
+ isPrometheusOperatorAlreadyInstalled = false
+
+ // projectImage is the name of the image which will be build and loaded
+ // with the code source changes to be tested.
+ projectImage = "example.com/project:v0.0.1"
+)
+
+// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated,
+// temporary environment to validate project changes with the purpose of being used in CI jobs.
+// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs
+// CertManager.
+func TestE2E(t *testing.T) {
+ RegisterFailHandler(Fail)
+ _, _ = fmt.Fprintf(GinkgoWriter, "Starting project integration test suite\n")
+ RunSpecs(t, "e2e suite")
+}
+
+var _ = BeforeSuite(func() {
+ By("Ensure that Prometheus is enabled")
+ _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#")
+
+ By("building the manager(Operator) image")
+ cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
+ _, err := utils.Run(cmd)
+ ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")
+
+ // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
+ // built and available before running the tests. Also, remove the following block.
+ By("loading the manager(Operator) image on Kind")
+ err = utils.LoadImageToKindClusterWithName(projectImage)
+ ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind")
+
+ // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing.
+ // To prevent errors when tests run in environments with Prometheus already installed,
+ // we check for its presence before execution.
+ // Setup Prometheus before the suite if not already installed
+ By("checking if prometheus is installed already")
+ isPrometheusOperatorAlreadyInstalled = utils.IsPrometheusCRDsInstalled()
+ if !isPrometheusOperatorAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n")
+ Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator")
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: Prometheus Operator is already installed. Skipping installation...\n")
+ }
+
+ // To prevent errors when tests run in environments with CertManager already installed,
+ // we check for its presence before execution.
+ // Setup CertManager before the suite if not skipped and if not already installed
+ if !skipCertManagerInstall {
+ By("checking if cert manager is installed already")
+ isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled()
+ if !isCertManagerAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n")
+ Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager")
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n")
+ }
+ }
+})
+
+var _ = AfterSuite(func() {
+ // Teardown Prometheus after the suite if it was not already installed
+ if !isPrometheusOperatorAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n")
+ utils.UninstallPrometheusOperator()
+ }
+
+ // Teardown CertManager after the suite if not skipped and if it was not already installed
+ if !skipCertManagerInstall && !isCertManagerAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n")
+ utils.UninstallCertManager()
+ }
+})
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
new file mode 100644
index 00000000000..adff96390cc
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/test/e2e/e2e_test.go
@@ -0,0 +1,391 @@
+//go:build e2e
+// +build e2e
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "tutorial.kubebuilder.io/project/test/utils"
+)
+
+// namespace where the project is deployed in
+const namespace = "project-system"
+
+// serviceAccountName created for the project
+const serviceAccountName = "project-controller-manager"
+
+// metricsServiceName is the name of the metrics service of the project
+const metricsServiceName = "project-controller-manager-metrics-service"
+
+// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data
+const metricsRoleBindingName = "project-metrics-binding"
+
+var _ = Describe("Manager", Ordered, func() {
+ var controllerPodName string
+
+ // Before running the tests, set up the environment by creating the namespace,
+ // 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)
+ Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs")
+
+ By("deploying the controller-manager")
+ cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage))
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager")
+ })
+
+ // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs,
+ // and deleting the namespace.
+ AfterAll(func() {
+ By("cleaning up the curl pod for metrics")
+ cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace)
+ _, _ = utils.Run(cmd)
+
+ By("undeploying the controller-manager")
+ cmd = exec.Command("make", "undeploy")
+ _, _ = utils.Run(cmd)
+
+ By("uninstalling CRDs")
+ cmd = exec.Command("make", "uninstall")
+ _, _ = utils.Run(cmd)
+
+ By("removing manager namespace")
+ cmd = exec.Command("kubectl", "delete", "ns", namespace)
+ _, _ = utils.Run(cmd)
+ })
+
+ // After each test, check for failures and collect logs, events,
+ // and pod descriptions for debugging.
+ AfterEach(func() {
+ specReport := CurrentSpecReport()
+ if specReport.Failed() {
+ By("Fetching controller manager pod logs")
+ cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
+ controllerLogs, err := utils.Run(cmd)
+ if err == nil {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs)
+ } else {
+ _, _ = 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, "Kubernetes events:\n%s", eventsOutput)
+ } else {
+ _, _ = 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, "Metrics logs:\n %s", metricsOutput)
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err)
+ }
+
+ By("Fetching controller manager pod description")
+ cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace)
+ podDescription, err := utils.Run(cmd)
+ if err == nil {
+ fmt.Println("Pod description:\n", podDescription)
+ } else {
+ fmt.Println("Failed to describe controller pod")
+ }
+ }
+ })
+
+ SetDefaultEventuallyTimeout(2 * time.Minute)
+ SetDefaultEventuallyPollingInterval(time.Second)
+
+ Context("Manager", func() {
+ It("should run successfully", func() {
+ By("validating that the controller-manager pod is running as expected")
+ verifyControllerUp := func(g Gomega) {
+ // Get the name of the controller-manager pod
+ cmd := exec.Command("kubectl", "get",
+ "pods", "-l", "control-plane=controller-manager",
+ "-o", "go-template={{ range .items }}"+
+ "{{ if not .metadata.deletionTimestamp }}"+
+ "{{ .metadata.name }}"+
+ "{{ \"\\n\" }}{{ end }}{{ end }}",
+ "-n", namespace,
+ )
+
+ podOutput, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information")
+ podNames := utils.GetNonEmptyLines(podOutput)
+ g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running")
+ controllerPodName = podNames[0]
+ g.Expect(controllerPodName).To(ContainSubstring("controller-manager"))
+
+ // Validate the pod's status
+ cmd = exec.Command("kubectl", "get",
+ "pods", controllerPodName, "-o", "jsonpath={.status.phase}",
+ "-n", namespace,
+ )
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status")
+ }
+ Eventually(verifyControllerUp).Should(Succeed())
+ })
+
+ It("should ensure the metrics endpoint is serving metrics", func() {
+ By("creating a ClusterRoleBinding for the service account to allow access to metrics")
+ cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName,
+ "--clusterrole=project-metrics-reader",
+ fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName),
+ )
+ _, err := utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding")
+
+ By("validating that the metrics service is available")
+ cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace)
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Metrics service should exist")
+
+ By("validating that the ServiceMonitor for Prometheus is applied in the namespace")
+ cmd = exec.Command("kubectl", "get", "ServiceMonitor", "-n", namespace)
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "ServiceMonitor should exist")
+
+ By("getting the service account token")
+ token, err := serviceAccountToken()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(token).NotTo(BeEmpty())
+
+ By("ensuring the controller pod is ready")
+ verifyControllerPodReady := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "pod", controllerPodName, "-n", namespace,
+ "-o", "jsonpath={.status.conditions[?(@.type=='Ready')].status}")
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("True"), "Controller pod not ready")
+ }
+ Eventually(verifyControllerPodReady, 3*time.Minute, time.Second).Should(Succeed())
+
+ By("verifying that the controller manager is serving the metrics server")
+ verifyMetricsServerStarted := func(g Gomega) {
+ cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(ContainSubstring("Serving metrics server"),
+ "Metrics server not yet started")
+ }
+ Eventually(verifyMetricsServerStarted, 3*time.Minute, time.Second).Should(Succeed())
+
+ By("waiting for the webhook service endpoints to be ready")
+ verifyWebhookEndpointsReady := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "endpointslices.discovery.k8s.io", "-n", namespace,
+ "-l", "kubernetes.io/service-name=project-webhook-service",
+ "-o", "jsonpath={range .items[*]}{range .endpoints[*]}{.addresses[*]}{end}{end}")
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Webhook endpoints should exist")
+ g.Expect(output).ShouldNot(BeEmpty(), "Webhook endpoints not yet ready")
+ }
+ Eventually(verifyWebhookEndpointsReady, 3*time.Minute, time.Second).Should(Succeed())
+
+ // +kubebuilder:scaffold:e2e-metrics-webhooks-readiness
+
+ 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: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": {
+ "readOnlyRootFilesystem": true,
+ "allowPrivilegeEscalation": false,
+ "capabilities": {
+ "drop": ["ALL"]
+ },
+ "runAsNonRoot": true,
+ "runAsUser": 1000,
+ "seccompProfile": {
+ "type": "RuntimeDefault"
+ }
+ }
+ }],
+ "serviceAccountName": "%s"
+ }
+ }`, token, metricsServiceName, namespace, serviceAccountName))
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod")
+
+ By("waiting for the curl-metrics pod to complete.")
+ verifyCurlUp := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "pods", "curl-metrics",
+ "-o", "jsonpath={.status.phase}",
+ "-n", namespace)
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status")
+ }
+ Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed())
+
+ By("getting the metrics by checking curl-metrics logs")
+ verifyMetricsAvailable := func(g Gomega) {
+ metricsOutput, err := getMetricsOutput()
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
+ g.Expect(metricsOutput).NotTo(BeEmpty())
+ g.Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK"))
+ }
+ Eventually(verifyMetricsAvailable, 2*time.Minute).Should(Succeed())
+ })
+
+ It("should provisioned cert-manager", func() {
+ By("validating that cert-manager has the certificate Secret")
+ verifyCertManager := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "secrets", "webhook-server-cert", "-n", namespace)
+ _, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ }
+ Eventually(verifyCertManager).Should(Succeed())
+ })
+
+ It("should have CA injection for mutating webhooks", func() {
+ By("checking CA injection for mutating webhooks")
+ verifyCAInjection := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get",
+ "mutatingwebhookconfigurations.admissionregistration.k8s.io",
+ "project-mutating-webhook-configuration",
+ "-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
+ mwhOutput, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(len(mwhOutput)).To(BeNumerically(">", 10))
+ }
+ Eventually(verifyCAInjection).Should(Succeed())
+ })
+
+ It("should have CA injection for validating webhooks", func() {
+ By("checking CA injection for validating webhooks")
+ verifyCAInjection := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get",
+ "validatingwebhookconfigurations.admissionregistration.k8s.io",
+ "project-validating-webhook-configuration",
+ "-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
+ 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.
+ // Consider applying sample/CR(s) and check their status and/or verifying
+ // the reconciliation by using the metrics, i.e.:
+ // metricsOutput, err := getMetricsOutput()
+ // Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
+ // Expect(metricsOutput).To(ContainSubstring(
+ // fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`,
+ // strings.ToLower(),
+ // ))
+ })
+})
+
+// serviceAccountToken returns a token for the specified service account in the given namespace.
+// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request
+// and parsing the resulting token from the API response.
+func serviceAccountToken() (string, error) {
+ const tokenRequestRawString = `{
+ "apiVersion": "authentication.k8s.io/v1",
+ "kind": "TokenRequest"
+ }`
+
+ // Temporary file to store the token request
+ secretName := fmt.Sprintf("%s-token-request", serviceAccountName)
+ tokenRequestFile := filepath.Join("/tmp", secretName)
+ err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644))
+ if err != nil {
+ return "", err
+ }
+
+ var out string
+ verifyTokenCreation := func(g Gomega) {
+ // Execute kubectl command to create the token
+ cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf(
+ "/api/v1/namespaces/%s/serviceaccounts/%s/token",
+ namespace,
+ serviceAccountName,
+ ), "-f", tokenRequestFile)
+
+ output, err := cmd.CombinedOutput()
+ g.Expect(err).NotTo(HaveOccurred())
+
+ // Parse the JSON output to extract the token
+ var token tokenRequest
+ err = json.Unmarshal(output, &token)
+ g.Expect(err).NotTo(HaveOccurred())
+
+ out = token.Status.Token
+ }
+ Eventually(verifyTokenCreation).Should(Succeed())
+
+ return out, err
+}
+
+// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint.
+func getMetricsOutput() (string, error) {
+ By("getting the curl-metrics logs")
+ cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace)
+ return utils.Run(cmd)
+}
+
+// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response,
+// containing only the token field that we need to extract.
+type tokenRequest struct {
+ Status struct {
+ Token string `json:"token"`
+ } `json:"status"`
+}
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
new file mode 100644
index 00000000000..2653bcd6c8c
--- /dev/null
+++ b/docs/book/src/cronjob-tutorial/testdata/project/test/utils/utils.go
@@ -0,0 +1,274 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package utils
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck
+)
+
+const (
+ certmanagerVersion = "v1.19.1"
+ certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
+
+ defaultKindBinary = "kind"
+ defaultKindCluster = "kind"
+
+ prometheusOperatorVersion = "v0.85.0"
+ prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" +
+ "releases/download/%s/bundle.yaml"
+)
+
+func warnError(err error) {
+ _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
+}
+
+// Run executes the provided command within this context
+func Run(cmd *exec.Cmd) (string, error) {
+ dir, _ := GetProjectDir()
+ cmd.Dir = dir
+
+ if err := os.Chdir(cmd.Dir); err != nil {
+ _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err)
+ }
+
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+ command := strings.Join(cmd.Args, " ")
+ _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err)
+ }
+
+ return string(output), nil
+}
+
+// UninstallCertManager uninstalls the cert manager
+func UninstallCertManager() {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+
+ // Delete leftover leases in kube-system (not cleaned by default)
+ kubeSystemLeases := []string{
+ "cert-manager-cainjector-leader-election",
+ "cert-manager-controller",
+ }
+ for _, lease := range kubeSystemLeases {
+ cmd = exec.Command("kubectl", "delete", "lease", lease,
+ "-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+ }
+}
+
+// InstallCertManager installs the cert manager bundle.
+func InstallCertManager() error {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "apply", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ return err
+ }
+ // Wait for cert-manager-webhook to be ready, which can take time if cert-manager
+ // was re-installed after uninstalling on a cluster.
+ cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
+ "--for", "condition=Available",
+ "--namespace", "cert-manager",
+ "--timeout", "5m",
+ )
+
+ _, err := Run(cmd)
+ return err
+}
+
+// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed
+// by verifying the existence of key CRDs related to Cert Manager.
+func IsCertManagerCRDsInstalled() bool {
+ // List of common Cert Manager CRDs
+ certManagerCRDs := []string{
+ "certificates.cert-manager.io",
+ "issuers.cert-manager.io",
+ "clusterissuers.cert-manager.io",
+ "certificaterequests.cert-manager.io",
+ "orders.acme.cert-manager.io",
+ "challenges.acme.cert-manager.io",
+ }
+
+ // Execute the kubectl command to get all CRDs
+ cmd := exec.Command("kubectl", "get", "crds")
+ output, err := Run(cmd)
+ if err != nil {
+ return false
+ }
+
+ // Check if any of the Cert Manager CRDs are present
+ crdList := GetNonEmptyLines(output)
+ for _, crd := range certManagerCRDs {
+ for _, line := range crdList {
+ if strings.Contains(line, crd) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics.
+func InstallPrometheusOperator() error {
+ url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
+ cmd := exec.Command("kubectl", "create", "-f", url)
+ _, err := Run(cmd)
+ return err
+}
+
+// UninstallPrometheusOperator uninstalls the prometheus
+func UninstallPrometheusOperator() {
+ url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+}
+
+// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed
+// by verifying the existence of key CRDs related to Prometheus.
+func IsPrometheusCRDsInstalled() bool {
+ // List of common Prometheus CRDs
+ prometheusCRDs := []string{
+ "prometheuses.monitoring.coreos.com",
+ "prometheusrules.monitoring.coreos.com",
+ "prometheusagents.monitoring.coreos.com",
+ }
+
+ cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name")
+ output, err := Run(cmd)
+ if err != nil {
+ return false
+ }
+ crdList := GetNonEmptyLines(output)
+ for _, crd := range prometheusCRDs {
+ for _, line := range crdList {
+ if strings.Contains(line, crd) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// LoadImageToKindClusterWithName loads a local docker image to the kind cluster
+func LoadImageToKindClusterWithName(name string) error {
+ cluster := defaultKindCluster
+ if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
+ cluster = v
+ }
+ kindOptions := []string{"load", "docker-image", name, "--name", cluster}
+ kindBinary := defaultKindBinary
+ if v, ok := os.LookupEnv("KIND"); ok {
+ kindBinary = v
+ }
+ cmd := exec.Command(kindBinary, kindOptions...)
+ _, err := Run(cmd)
+ return err
+}
+
+// GetNonEmptyLines converts given command output string into individual objects
+// according to line breakers, and ignores the empty elements in it.
+func GetNonEmptyLines(output string) []string {
+ var res []string
+ elements := strings.Split(output, "\n")
+ for _, element := range elements {
+ if element != "" {
+ res = append(res, element)
+ }
+ }
+
+ return res
+}
+
+// GetProjectDir will return the directory where the project is
+func GetProjectDir() (string, error) {
+ wd, err := os.Getwd()
+ if err != nil {
+ return wd, fmt.Errorf("failed to get current working directory: %w", err)
+ }
+ wd = strings.ReplaceAll(wd, "/test/e2e", "")
+ return wd, nil
+}
+
+// UncommentCode searches for target in the file and remove the comment prefix
+// of the target content. The target content may span multiple lines.
+func UncommentCode(filename, target, prefix string) error {
+ // false positive
+ // nolint:gosec
+ content, err := os.ReadFile(filename)
+ if err != nil {
+ return fmt.Errorf("failed to read file %q: %w", filename, err)
+ }
+ strContent := string(content)
+
+ idx := strings.Index(strContent, target)
+ if idx < 0 {
+ return fmt.Errorf("unable to find the code %q to be uncomment", target)
+ }
+
+ out := new(bytes.Buffer)
+ _, err = out.Write(content[:idx])
+ if err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+
+ scanner := bufio.NewScanner(bytes.NewBufferString(target))
+ if !scanner.Scan() {
+ return nil
+ }
+ for {
+ if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+ // Avoid writing a newline in case the previous line was the last in target.
+ if !scanner.Scan() {
+ break
+ }
+ if _, err = out.WriteString("\n"); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+ }
+
+ if _, err = out.Write(content[idx+len(target):]); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+
+ // false positive
+ // nolint:gosec
+ if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil {
+ return fmt.Errorf("failed to write file %q: %w", filename, err)
+ }
+
+ return nil
+}
diff --git a/docs/book/src/cronjob-tutorial/webhook-implementation.md b/docs/book/src/cronjob-tutorial/webhook-implementation.md
index 756bbb1474b..ea4b4375a42 100644
--- a/docs/book/src/cronjob-tutorial/webhook-implementation.md
+++ b/docs/book/src/cronjob-tutorial/webhook-implementation.md
@@ -1,8 +1,8 @@
# Implementing defaulting/validating webhooks
If you want to implement [admission webhooks](../reference/admission-webhook.md)
-for your CRD, the only thing you need to do is to implement the `Defaulter`
-and (or) the `Validator` interface.
+for your CRD, the only thing you need to do is to implement the `CustomDefaulter`
+and (or) the `CustomValidator` interface.
Kubebuilder takes care of the rest for you, such as
@@ -11,7 +11,7 @@ Kubebuilder takes care of the rest for you, such as
1. Creating handlers for your webhooks.
1. Registering each handler with a path in your server.
-First, let's scaffold the webhooks for our CRD (CronJob). We’ll need to run the following command with the `--defaulting` and `--programmatic-validation` flags (since our test project will use defaulting and validating webhooks):
+First, let's scaffold the webhooks for our CRD (CronJob). We'll need to run the following command with the `--defaulting` and `--programmatic-validation` flags (since our test project will use defaulting and validating webhooks):
```bash
kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
@@ -19,17 +19,29 @@ kubebuilder create webhook --group batch --version v1 --kind CronJob --defaultin
This will scaffold the webhook functions and register your webhook with the manager in your `main.go` for you.
-
+## Custom Webhook Paths
+
+You can specify custom HTTP paths for your webhooks using the `--defaulting-path` and `--validation-path` flags:
+
+```bash
+# Custom path for defaulting webhook
+kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --defaulting-path=/my-custom-mutate-path
-Supporting older cluster versions
+# Custom path for validation webhook
+kubebuilder create webhook --group batch --version v1 --kind CronJob --programmatic-validation --validation-path=/my-custom-validate-path
-The default WebhookConfiguration manifests created alongside your Go webhook implementation
-use API version `v1`. If your project intends to support Kubernetes cluster versions older
-than v1.16, set `--webhook-version v1beta1`. See the [webhook reference][webhook-reference]
-for more information.
+# Both webhooks with different custom paths
+kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation \
+ --defaulting-path=/custom-mutate --validation-path=/custom-validate
+```
+
+This changes the path in the webhook marker annotation but does not change where the webhook files are scaffolded. The webhook files will still be created in `internal/webhook/v1/`.
+
+
+Version Requirements
-[webhook-reference]: /reference/webhook-overview.md#supporting-older-cluster-versions
+Custom webhook paths require **controller-runtime v0.21+**. In earlier versions (< `v0.21`), the webhook path must follow a specific pattern and cannot be customized. The path is automatically generated based on the resource's group, version, and kind (e.g., `/mutate-batch-v1-cronjob`).
-{{#literatego ./testdata/project/api/v1/cronjob_webhook.go}}
+{{#literatego ./testdata/project/internal/webhook/v1/cronjob_webhook.go}}
diff --git a/docs/book/src/cronjob-tutorial/writing-tests.md b/docs/book/src/cronjob-tutorial/writing-tests.md
index e62d8a31441..827e57b01d3 100644
--- a/docs/book/src/cronjob-tutorial/writing-tests.md
+++ b/docs/book/src/cronjob-tutorial/writing-tests.md
@@ -11,11 +11,11 @@ If you want to tinker with how your envtest cluster is configured, see section [
## Test Environment Setup
-{{#literatego ../cronjob-tutorial/testdata/project/controllers/suite_test.go}}
+{{#literatego ../cronjob-tutorial/testdata/project/internal/controller/suite_test.go}}
## Testing your Controller's Behavior
-{{#literatego ../cronjob-tutorial/testdata/project/controllers/cronjob_controller_test.go}}
+{{#literatego ../cronjob-tutorial/testdata/project/internal/controller/cronjob_controller_test.go}}
This Status update example above demonstrates a general testing strategy for a custom Kind with downstream objects. By this point, you hopefully have learned the following methods for testing your controller behavior:
@@ -23,11 +23,11 @@ This Status update example above demonstrates a general testing strategy for a c
* Writing stubs for creating test objects
* Isolating changes to an object to test specific controller behavior
-## Advanced Examples
+
+Examples
-There are more involved examples of using envtest to rigorously test controller behavior. Examples include:
+You can use the plugin [DeployImage](../plugins/available/deploy-image-plugin-v1-alpha.md) to check examples. This plugin allows users to scaffold API/Controllers to deploy and manage an Operand (image) on the cluster following the guidelines and best practices. It abstracts the complexities of achieving this goal while allowing users to customize the generated code.
-* Azure Databricks Operator: see their fully fleshed-out
- [`suite_test.go`](https://github.com/microsoft/azure-databricks-operator/blob/0f722a710fea06b86ecdccd9455336ca712bf775/controllers/suite_test.go)
- as well as any `*_test.go` file in that directory [like this
- one](https://github.com/microsoft/azure-databricks-operator/blob/0f722a710fea06b86ecdccd9455336ca712bf775/controllers/secretscope_controller_test.go).
+Therefore, you can check that a test using ENV TEST will be generated for the controller which has the purpose to ensure that the Deployment is created successfully. You can see an example of its code implementation under the `testdata` directory with the [DeployImage](../plugins/available/deploy-image-plugin-v1-alpha.md) samples [here](https://github.com/kubernetes-sigs/kubebuilder/blob/master/testdata/project-v4-with-plugins/internal/controller/busybox_controller_test.go).
+
+
diff --git a/docs/book/src/faq.md b/docs/book/src/faq.md
new file mode 100644
index 00000000000..3b7a55d7843
--- /dev/null
+++ b/docs/book/src/faq.md
@@ -0,0 +1,171 @@
+
+# FAQ
+
+
+ Controller-Runtime FAQ
+
+Kubebuilder is developed on top of the [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime)
+and [controller-tools](https://github.com/kubernetes-sigs/controller-tools) libraries. We recommend you also check
+the [Controller-Runtime FAQ page](https://github.com/kubernetes-sigs/controller-runtime/blob/main/FAQ.md).
+
+
+
+## How does the value informed via the domain flag (i.e. `kubebuilder init --domain example.com`) when we init a project?
+
+After creating a project, usually you will want to extend the Kubernetes APIs and define new APIs which will be owned by your project. Therefore, the domain value is tracked in the [PROJECT][project-file-def] file which defines the config of your project and will be used as a domain to create the endpoints of your API(s). Please, ensure that you understand the [Groups and Versions and Kinds, oh my!][gvk].
+
+The domain is for the group suffix, to explicitly show the resource group category.
+For example, if set `--domain=example.com`:
+```
+kubebuilder init --domain example.com --repo xxx --plugins=go/v4
+kubebuilder create api --group mygroup --version v1beta1 --kind Mykind
+```
+Then the result resource group will be `mygroup.example.com`.
+
+> If domain field not set, the default value is `my.domain`.
+
+## I'd like to customize my project to use [klog][klog] instead of the [zap][zap] provided by controller-runtime. How to use `klog` or other loggers as the project logger?
+
+In the `main.go` you can replace:
+```go
+ opts := zap.Options{
+ Development: true,
+ }
+ opts.BindFlags(flag.CommandLine)
+ flag.Parse()
+
+ ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+```
+with:
+```go
+ flag.Parse()
+ ctrl.SetLogger(klog.NewKlogr())
+```
+
+## After `make run`, I see errors like "unable to find leader election namespace: not running in-cluster..."
+
+You can enable the leader election. However, if you are testing the project locally using the `make run`
+target which will run the manager outside of the cluster then, you might also need to set the
+namespace the leader election resource will be created, as follows:
+```go
+mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ MetricsBindAddress: metricsAddr,
+ Port: 9443,
+ HealthProbeBindAddress: probeAddr,
+ LeaderElection: enableLeaderElection,
+ LeaderElectionID: "14be1926.testproject.org",
+ LeaderElectionNamespace: "-system",
+```
+
+If you are running the project on the cluster with `make deploy` target
+then, you might not want to add this option. So, you might want to customize this behaviour using
+environment variables to only add this option for development purposes, such as:
+
+```go
+ leaderElectionNS := ""
+ if os.Getenv("ENABLE_LEADER_ELECTION_NAMESPACE") != "false" {
+ leaderElectionNS = "-system"
+ }
+
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ MetricsBindAddress: metricsAddr,
+ Port: 9443,
+ HealthProbeBindAddress: probeAddr,
+ LeaderElection: enableLeaderElection,
+ LeaderElectionNamespace: leaderElectionNS,
+ LeaderElectionID: "14be1926.testproject.org",
+ ...
+```
+
+## I am facing the error "open /var/run/secrets/kubernetes.io/serviceaccount/token: permission denied" when I deploy my project against Kubernetes old versions. How to sort it out?
+
+If you are facing the error:
+```
+1.6656687258729894e+09 ERROR controller-runtime.client.config unable to get kubeconfig {"error": "open /var/run/secrets/kubernetes.io/serviceaccount/token: permission denied"}
+sigs.k8s.io/controller-runtime/pkg/client/config.GetConfigOrDie
+ /go/pkg/mod/sigs.k8s.io/controller-runtime@v0.13.0/pkg/client/config/config.go:153
+main.main
+ /workspace/main.go:68
+runtime.main
+ /usr/local/go/src/runtime/proc.go:250
+```
+when you are running the project against a Kubernetes old version (maybe <= 1.21) , it might be caused by the [issue][permission-issue] , the reason is the mounted token file set to `0600`, see [solution][permission-PR] here. Then, the workaround is:
+
+Add `fsGroup` in the manager.yaml
+```yaml
+securityContext:
+ runAsNonRoot: true
+ fsGroup: 65532 # add this fsGroup to make the token file readable
+```
+However, note that this problem is fixed and will not occur if you deploy the project in high versions (maybe >= 1.22).
+
+## The error `Too long: must have at most 262144 bytes` is faced when I run `make install` to apply the CRD manifests. How to solve it? Why this error is faced?
+
+When attempting to run `make install` to apply the CRD manifests, the error `Too long: must have at most 262144 bytes may be encountered.` This error arises due to a size limit enforced by the Kubernetes API. Note that the `make install` target will apply the CRD manifest under `config/crd` using `kubectl apply -f -`. Therefore, when the apply command is used, the API annotates the object with the `last-applied-configuration` which contains the entire previous configuration. If this configuration is too large, it will exceed the allowed byte size. ([More info][k8s-obj-creation])
+
+In ideal approach might use client-side apply might seem like the perfect solution since with the entire object configuration doesn't have to be stored as an annotation (last-applied-configuration) on the server. However, it's worth noting that as of now, it isn't supported by controller-gen or kubebuilder. For more on this, refer to: [Controller-tool-discussion][controller-tool-pr].
+
+Therefore, you have a few options to workround this scenario such as:
+
+**By removing the descriptions from CRDs:**
+
+Your CRDs are generated using [controller-gen][controller-gen]. By using the option `maxDescLen=0` to remove the description, you may reduce the size, potentially resolving the issue. To do it you can update the Makefile as the following example and then, call the target `make manifest` to regenerate your CRDs without description, see:
+
+```shell
+
+ .PHONY: manifests
+ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
+ # Note that the option maxDescLen=0 was added in the default scaffold in order to sort out the issue
+ # Too long: must have at most 262144 bytes. By using kubectl apply to create / update resources an annotation
+ # is created by K8s API to store the latest version of the resource ( kubectl.kubernetes.io/last-applied-configuration).
+ # However, it has a size limit and if the CRD is too big with so many long descriptions as this one it will cause the failure.
+ $(CONTROLLER_GEN) rbac:roleName=manager-role crd:maxDescLen=0 webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+```
+**By re-design your APIs:**
+
+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.
+
+
+
+[k8s-obj-creation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#how-to-create-objects
+[gvk]: ./cronjob-tutorial/gvks.md
+[project-file-def]: ./reference/project-config.md
+[klog]: https://github.com/kubernetes/klog
+[zap]: https://github.com/uber-go/zap
+[permission-issue]: https://github.com/kubernetes/kubernetes/issues/82573
+[permission-PR]: https://github.com/kubernetes/kubernetes/pull/89193
+[controller-gen]: ./reference/controller-gen.html
+[controller-tool-pr]: https://github.com/kubernetes-sigs/controller-tools/pull/536
\ No newline at end of file
diff --git a/docs/book/src/getting-started.md b/docs/book/src/getting-started.md
new file mode 100644
index 00000000000..1bec661caff
--- /dev/null
+++ b/docs/book/src/getting-started.md
@@ -0,0 +1,463 @@
+# Getting Started
+
+We will create a sample project to let you know how it works. This sample will:
+
+- Reconcile a Memcached CR - which represents an instance of a Memcached deployed/managed on cluster
+- Create a Deployment with the Memcached image
+- Not allow more instances than the size defined in the CR which will be applied
+- Update the Memcached CR status
+
+
+Why Operators?
+
+By following the [Operator Pattern][k8s-operator-pattern], it’s possible not only to provide all expected resources
+but also to manage them dynamically, programmatically, and at execution time. To illustrate this idea, imagine if
+someone accidentally changed a configuration or removed a resource by mistake; in this case, the operator could fix it
+without any human intervention.
+
+
+
+
+Following Along vs Jumping Ahead
+
+Note that most of this tutorial is generated from literate Go files that
+form a runnable project, and live in the book source directory:
+[docs/book/src/getting-started/testdata/project][tutorial-source].
+
+[tutorial-source]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/docs/book/src/cronjob-tutorial/testdata/project
+
+
+
+## Create a project
+
+First, create and navigate into a directory for your project. Then, initialize it using `kubebuilder`:
+
+```shell
+mkdir $GOPATH/memcached-operator
+cd $GOPATH/memcached-operator
+kubebuilder init --domain=example.com
+```
+
+
+Developing in $GOPATH
+
+If your project is initialized within [`GOPATH`][GOPATH-golang-docs], the implicitly called `go mod init` will interpolate the module path for you.
+Otherwise `--repo=` must be set.
+
+Read the [Go modules blogpost][go-modules-blogpost] if unfamiliar with the module system.
+
+
+
+## Create the Memcached API (CRD):
+
+Next, we'll create the API which will be responsible for deploying and
+managing Memcached(s) instances on the cluster.
+
+```shell
+kubebuilder create api --group cache --version v1alpha1 --kind Memcached
+```
+
+### Understanding APIs
+
+This command's primary aim is to produce the Custom Resource (CR) and Custom Resource Definition (CRD) for the Memcached Kind.
+It creates the API with the group `cache.example.com` and version `v1alpha1`, uniquely identifying the new CRD of the Memcached Kind.
+By leveraging the Kubebuilder tool, we can define our APIs and objects representing our solutions for these platforms.
+
+While we've added only one Kind of resource in this example, we can have as many `Groups` and `Kinds` as necessary.
+To make it easier to understand, think of CRDs as the definition of our custom Objects, while CRs are instances of them.
+
+
+ Please ensure that you check
+
+[Groups and Versions and Kinds, oh my!][group-kind-oh-my].
+
+
+
+### Defining our API
+
+#### Defining the Specs
+
+Now, we will define the values that each instance of your Memcached resource on the cluster can assume. In this example,
+we will allow configuring the number of instances with the following:
+
+```go
+type MemcachedSpec struct {
+ ...
+ // +kubebuilder:validation:Minimum=0
+ // +required
+ Size *int32 `json:"size,omitempty"`
+}
+```
+
+#### Creating Status definitions
+
+We also want to track the status of our Operations which will be done to manage the Memcached CR(s).
+This allows us to verify the Custom Resource's description of our own API and determine if everything
+occurred successfully or if any errors were encountered,
+similar to how we do with any resource from the Kubernetes API.
+
+```go
+// MemcachedStatus defines the observed state of Memcached
+type MemcachedStatus struct {
+ // +listType=map
+ // +listMapKey=type
+ // +optional
+ Conditions []metav1.Condition `json:"conditions,omitempty"`
+}
+```
+
+
+ Status Conditions
+
+Kubernetes has established conventions, and because of this, we use
+Status Conditions here. We want our custom APIs and controllers to behave
+like Kubernetes resources and their controllers, following these standards
+to ensure a consistent and intuitive experience.
+
+Please ensure that you review: [Kubernetes API Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties)
+
+
+
+#### Markers and validations
+
+Furthermore, we want to validate the values added in our CustomResource
+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.
+
+{{#literatego ./getting-started/testdata/project/api/v1alpha1/memcached_types.go}}
+
+#### Generating manifests with the specs and validations
+
+To generate all required files:
+
+1. Run `make generate` to create the DeepCopy implementations in `api/v1alpha1/zz_generated.deepcopy.go`.
+
+2. Then, run `make manifests` to generate the CRD manifests under `config/crd/bases` and a sample for it under `config/samples`.
+
+Both commands use [controller-gen][controller-gen] with different flags for code and manifest generation, respectively.
+
+config/crd/bases/cache.example.com_memcacheds.yaml: Our Memcached CRD
+
+```yaml
+{{#include ./getting-started/testdata/project/config/crd/bases/cache.example.com_memcacheds.yaml}}
+```
+
+
+
+#### Sample of Custom Resources
+
+The manifests located under the `config/samples` directory serve as examples of Custom Resources that can be applied to the cluster.
+In this particular example, by applying the given resource to the cluster, we would generate
+a Deployment with a single instance size (see `size: 1`).
+
+```yaml
+{{#include ./getting-started/testdata/project/config/samples/cache_v1alpha1_memcached.yaml}}
+```
+
+### Reconciliation Process
+
+In a simplified way, Kubernetes works by allowing us to declare the desired state of our system, and then its controllers continuously observe the cluster and take actions to ensure that the actual state matches the desired state. For our custom APIs and controllers, the process is similar. Remember, we are extending Kubernetes' behaviors and its APIs to fit our specific needs.
+
+In our controller, we will implement a reconciliation process.
+
+Essentially, the reconciliation process functions as a loop, continuously checking conditions and performing necessary actions until the desired state is achieved. This process will keep running until all conditions in the system align with the desired state defined in our implementation.
+
+Here's a pseudo-code example to illustrate this:
+
+```go
+reconcile App {
+
+ // Check if a Deployment for the app exists, if not, create one
+ // If there's an error, then restart from the beginning of the reconcile
+ if err != nil {
+ return reconcile.Result{}, err
+ }
+
+ // Check if a Service for the app exists, if not, create one
+ // If there's an error, then restart from the beginning of the reconcile
+ if err != nil {
+ return reconcile.Result{}, err
+ }
+
+ // Look for Database CR/CRD
+ // Check the Database Deployment's replicas size
+ // If deployment.replicas size doesn't match cr.size, then update it
+ // Then, restart from the beginning of the reconcile. For example, by returning `reconcile.Result{Requeue: true}, nil`.
+ if err != nil {
+ return reconcile.Result{Requeue: true}, nil
+ }
+ ...
+
+ // If at the end of the loop:
+ // Everything was executed successfully, and the reconcile can stop
+ return reconcile.Result{}, nil
+
+}
+```
+
+
+ Return Options
+
+The following are a few possible return options to restart the Reconcile:
+
+- With the error:
+
+```go
+return ctrl.Result{}, err
+```
+- Without an error:
+
+```go
+return ctrl.Result{Requeue: true}, nil
+```
+
+- Therefore, to stop the Reconcile, use:
+
+```go
+return ctrl.Result{}, nil
+```
+
+- Reconcile again after X time:
+
+```go
+return ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())}, nil
+```
+
+
+
+#### In the context of our example
+
+When our sample Custom Resource (CR) is applied to the cluster (i.e. `kubectl apply -f config/sample/cache_v1alpha1_memcached.yaml`),
+we want to ensure that a Deployment is created for our Memcached image and that it matches the number of replicas defined in the CR.
+
+To achieve this, we need to first implement an operation that checks whether the Deployment for our Memcached instance already exists on the cluster.
+If it does not, the controller will create the Deployment accordingly. Therefore, our reconciliation process must include an operation to ensure that
+this desired state is consistently maintained. This operation would involve:
+
+```go
+ // Check if the deployment already exists, if not create a new one
+ found := &appsv1.Deployment{}
+ err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
+ if err != nil && apierrors.IsNotFound(err) {
+ // Define a new deployment
+ dep := r.deploymentForMemcached()
+ // Create the Deployment on the cluster
+ if err = r.Create(ctx, dep); err != nil {
+ log.Error(err, "Failed to create new Deployment",
+ "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
+ return ctrl.Result{}, err
+ }
+ ...
+ }
+```
+
+Next, note that the `deploymentForMemcached()` function will need to define and return the Deployment that should be
+created on the cluster. This function should construct the Deployment object with the necessary
+specifications, as demonstrated in the following example:
+
+```go
+ dep := &appsv1.Deployment{
+ Spec: appsv1.DeploymentSpec{
+ Replicas: &replicas,
+ Template: corev1.PodTemplateSpec{
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{{
+ Image: "memcached:1.6.26-alpine3.19",
+ Name: "memcached",
+ ImagePullPolicy: corev1.PullIfNotPresent,
+ Ports: []corev1.ContainerPort{{
+ ContainerPort: 11211,
+ Name: "memcached",
+ }},
+ Command: []string{"memcached", "--memory-limit=64", "-o", "modern", "-v"},
+ }},
+ },
+ },
+ },
+ }
+```
+
+Additionally, we need to implement a mechanism to verify that the number of Memcached replicas
+on the cluster matches the desired count specified in the Custom Resource (CR). If there is a
+discrepancy, the reconciliation must update the cluster to ensure consistency. This means that
+whenever a CR of the Memcached Kind is created or updated on the cluster, the controller will
+continuously reconcile the state until the actual number of replicas matches the desired count.
+The following example illustrates this process:
+
+```go
+ ...
+ size := memcached.Spec.Size
+ if *found.Spec.Replicas != size {
+ found.Spec.Replicas = &size
+ if err = r.Update(ctx, found); err != nil {
+ log.Error(err, "Failed to update Deployment",
+ "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
+ return ctrl.Result{}, err
+ }
+ ...
+```
+
+Now, you can review the complete controller responsible for managing Custom Resources of the
+Memcached Kind. This controller ensures that the desired state is maintained in the cluster,
+making sure that our Memcached instance continues running with the number of replicas specified
+by the users.
+
+internal/controller/memcached_controller.go: Our Controller Implementation
+
+```go
+{{#include ./getting-started/testdata/project/internal/controller/memcached_controller.go}}
+```
+
+
+### Diving Into the Controller Implementation
+
+#### Setting Manager to Watching Resources
+
+The whole idea is to be Watching the resources that matter for the controller.
+When a resource that the controller is interested in changes, the Watch triggers the controller's
+reconciliation loop, ensuring that the actual state of the resource matches the desired state
+as defined in the controller's logic.
+
+Notice how we configured the Manager to monitor events such as the creation, update, or deletion of a Custom Resource (CR) of the Memcached kind,
+as well as any changes to the Deployment that the controller manages and owns:
+
+```go
+// SetupWithManager sets up the controller with the Manager.
+// The Deployment is also watched to ensure its
+// desired state in the cluster.
+func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewControllerManagedBy(mgr).
+ // Watch the Memcached Custom Resource and trigger reconciliation whenever it
+ //is created, updated, or deleted
+ For(&cachev1alpha1.Memcached{}).
+ // Watch the Deployment managed by the Memcached controller. If any changes occur to the Deployment
+ // owned and managed by this controller, it will trigger reconciliation, ensuring that the cluster
+ // state aligns with the desired state.
+ Owns(&appsv1.Deployment{}).
+ Complete(r)
+ }
+```
+
+#### But, How Does the Manager Know Which Resources Are Owned by It?
+
+We do not want our Controller to watch any Deployment on the cluster and trigger our
+reconciliation loop. Instead, we only want to trigger reconciliation when the specific
+Deployment running our Memcached instance is changed. For example,
+if someone accidentally deletes our Deployment or changes the number of replicas, we want
+to trigger the reconciliation to ensure that it returns to the desired state.
+
+The Manager knows which Deployment to observe because we set the `ownerRef` (Owner Reference):
+
+```go
+if err := ctrl.SetControllerReference(memcached, dep, r.Scheme); err != nil {
+ return nil, err
+}
+```
+
+
+
+ownerRef and Cascading Events
+
+The ownerRef is crucial not only for allowing us to observe changes on the specific resource but also because,
+if we delete the Memcached Custom Resource (CR) from the cluster, we want all resources owned by it to be automatically
+deleted as well, in a cascading event.
+
+This ensures that when the parent resource (Memcached CR) is removed, all associated resources
+(like Deployments, Services, etc.) are also cleaned up, maintaining
+a tidy and consistent cluster state.
+
+For more information, see the Kubernetes documentation on [Owners and Dependents](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/).
+
+
+
+### Granting Permissions
+
+It's important to ensure that the Controller has the necessary permissions(i.e. to create, get, update, and list)
+the resources it manages.
+
+The [RBAC permissions][k8s-rbac] are now configured via [RBAC markers][rbac-markers], which are used to generate and update the
+manifest files present in `config/rbac/`. These markers can be found (and should be defined) on the `Reconcile()` method of each controller, see
+how it is implemented in our example:
+
+```go
+// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
+// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
+// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
+```
+
+After making changes to the controller, run the make manifests command. This will prompt [controller-gen][controller-gen]
+to refresh the files located under `config/rbac`.
+
+config/rbac/role.yaml: Our RBAC Role generated
+
+```yaml
+{{#include ./getting-started/testdata/project/config/rbac/role.yaml}}
+```
+
+
+### Manager (main.go)
+
+The [Manager][manager] in the `cmd/main.go` file is responsible for managing the controllers in your application.
+
+cmd/main.go: Our main.go
+
+```go
+{{#include ./getting-started/testdata/project/cmd/main.go}}
+```
+
+
+### Use Kubebuilder plugins to scaffold additional options
+
+Now that you have a better understanding of how to create your own API and controller,
+let’s scaffold in this project the plugin [`autoupdate.kubebuilder.io/v1-alpha`][autoupdate-plugin]
+so that your project can be kept up to date with the latest Kubebuilder releases scaffolding changes
+and consequently adopt improvements from the ecosystem.
+
+```shell
+kubebuilder edit --plugins="autoupdate/v1-alpha"
+```
+
+Inspect the file `.github/workflows/auto-update.yml` to see how it works.
+
+### Checking the Project running in the cluster
+
+At this point you can check the steps to validate the project
+on the cluster by looking the steps defined in the Quick Start,
+see: [Run It On the Cluster](./quick-start#run-it-on-the-cluster)
+
+## Next Steps
+
+- To delve deeper into developing your solution, consider going through the [CronJob Tutorial][cronjob-tutorial]
+- For insights on optimizing your approach, refer to the [Best Practices][best-practices] documentation.
+
+
+ Using Deploy Image plugin to generate APIs and source code
+
+Now that you have a better understanding, you might want to check out the [Deploy Image][deploy-image] Plugin.
+This plugin allows users to scaffold APIs/Controllers to deploy and manage an Operand (image) on the cluster.
+It will provide scaffolds similar to the ones in this guide, along with additional features such as tests
+implemented for your controller.
+
+
+
+[k8s-operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[group-kind-oh-my]: ./cronjob-tutorial/gvks.md
+[controller-gen]: ./reference/controller-gen.md
+[markers]: ./reference/markers.md
+[rbac-markers]: ./reference/markers/rbac.md
+[k8s-rbac]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
+[manager]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager
+[options-manager]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager#Options
+[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/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
+[autoupdate-plugin]: ./plugins/available/autoupdate-v1-alpha.md
\ No newline at end of file
diff --git a/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json b/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json
new file mode 100644
index 00000000000..a3ab7541cb6
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.devcontainer/devcontainer.json
@@ -0,0 +1,25 @@
+{
+ "name": "Kubebuilder DevContainer",
+ "image": "golang:1.24",
+ "features": {
+ "ghcr.io/devcontainers/features/docker-in-docker:2": {},
+ "ghcr.io/devcontainers/features/git:1": {}
+ },
+
+ "runArgs": ["--network=host"],
+
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.shell.linux": "/bin/bash"
+ },
+ "extensions": [
+ "ms-kubernetes-tools.vscode-kubernetes-tools",
+ "ms-azuretools.vscode-docker"
+ ]
+ }
+ },
+
+ "onCreateCommand": "bash .devcontainer/post-install.sh"
+}
+
diff --git a/docs/book/src/getting-started/testdata/project/.devcontainer/post-install.sh b/docs/book/src/getting-started/testdata/project/.devcontainer/post-install.sh
new file mode 100644
index 00000000000..67f3e97ab32
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.devcontainer/post-install.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -x
+
+curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-$(go env GOARCH)
+chmod +x ./kind
+mv ./kind /usr/local/bin/kind
+
+curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/$(go env GOARCH)
+chmod +x kubebuilder
+mv kubebuilder /usr/local/bin/
+
+KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
+curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/$(go env GOARCH)/kubectl"
+chmod +x kubectl
+mv kubectl /usr/local/bin/kubectl
+
+docker network create -d=bridge --subnet=172.19.0.0/24 kind
+
+kind version
+kubebuilder version
+docker --version
+go version
+kubectl version --client
diff --git a/docs/book/src/getting-started/testdata/project/.dockerignore b/docs/book/src/getting-started/testdata/project/.dockerignore
new file mode 100644
index 00000000000..9af8280793c
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.dockerignore
@@ -0,0 +1,11 @@
+# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
+# Ignore everything by default and re-include only needed files
+**
+
+# Re-include Go source files (but not *_test.go)
+!**/*.go
+**/*_test.go
+
+# Re-include Go module files
+!go.mod
+!go.sum
diff --git a/docs/book/src/getting-started/testdata/project/.github/workflows/auto_update.yml b/docs/book/src/getting-started/testdata/project/.github/workflows/auto_update.yml
new file mode 100644
index 00000000000..c8fecc13357
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.github/workflows/auto_update.yml
@@ -0,0 +1,74 @@
+name: Auto Update
+
+# The 'kubebuilder alpha update 'command requires write access to the repository to create a branch
+# with the update files and allow you to open a pull request using the link provided in the issue.
+# The branch created will be named in the format kubebuilder-update-from--to- by default.
+# To protect your codebase, please ensure that you have branch protection rules configured for your
+# main branches. This will guarantee that no one can bypass a review and push directly to a branch like 'main'.
+permissions:
+ contents: write
+ issues: write
+ models: read
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: "0 0 * * 2" # Every Tuesday at 00:00 UTC
+
+jobs:
+ auto-update:
+ runs-on: ubuntu-latest
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ # Step 1: Checkout the repository.
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ fetch-depth: 0
+
+ # Step 2: Configure Git to create commits with the GitHub Actions bot.
+ - name: Configure Git
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+
+ # Step 3: Set up Go environment.
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: stable
+
+ # Step 4: Install Kubebuilder.
+ - name: Install Kubebuilder
+ run: |
+ curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
+ chmod +x kubebuilder
+ sudo mv kubebuilder /usr/local/bin/
+ kubebuilder version
+
+ # Step 5: Install Models extension for GitHub CLI
+ - name: Install/upgrade gh-models extension
+ run: |
+ gh extension install github/gh-models --force
+ gh models --help >/dev/null
+
+ # Step 6: Run the Kubebuilder alpha update command.
+ # More info: https://kubebuilder.io/reference/commands/alpha_update
+ - name: Run kubebuilder alpha update
+ # Executes the update command with specified flags.
+ # --force: Completes the merge even if conflicts occur, leaving conflict markers.
+ # --push: Automatically pushes the resulting output branch to the 'origin' remote.
+ # --restore-path: Preserves specified paths (e.g., CI workflow files) when squashing.
+ # --open-gh-issue: Creates a GitHub Issue with a link for opening a PR for review.
+ # --use-gh-models: Adds an AI-generated comment to the created Issue with
+ # a short overview of the scaffold changes and conflict-resolution guidance (If Any).
+ run: |
+ kubebuilder alpha update \
+ --force \
+ --push \
+ --restore-path .github/workflows \
+ --open-gh-issue \
+ --use-gh-models
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
new file mode 100644
index 00000000000..029218b7232
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.github/workflows/lint.yml
@@ -0,0 +1,23 @@
+name: Lint
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ lint:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+
+ - name: Run linter
+ uses: golangci/golangci-lint-action@v8
+ with:
+ version: v2.6.0
diff --git a/docs/book/src/getting-started/testdata/project/.github/workflows/test-chart.yml b/docs/book/src/getting-started/testdata/project/.github/workflows/test-chart.yml
new file mode 100644
index 00000000000..2e4d59136a7
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.github/workflows/test-chart.yml
@@ -0,0 +1,74 @@
+name: Test Chart
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test-e2e:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ 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-$(go env GOARCH)
+ 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
+ run: |
+ go mod tidy
+ make docker-build IMG=project:v0.1.0
+ kind load docker-image project: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
+ run: |
+ helm lint ./dist/chart
+
+# TODO: Uncomment if cert-manager is enabled
+# - name: Install cert-manager via Helm (wait for readiness)
+# 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 crds.enabled=true \
+# --wait \
+# --timeout 300s
+
+# TODO: Uncomment if Prometheus is enabled
+# - name: Install Prometheus Operator CRDs
+# run: |
+# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
+# helm repo update
+# helm install prometheus-crds prometheus-community/prometheus-operator-crds
+
+ - name: Install Helm chart for project
+ run: |
+ helm install my-release ./dist/chart --create-namespace --namespace project-system
+
+ - name: Check Helm release status
+ run: |
+ helm status my-release --namespace project-system
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
new file mode 100644
index 00000000000..4cdfb30e026
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.github/workflows/test-e2e.yml
@@ -0,0 +1,32 @@
+name: E2E Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test-e2e:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ 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-$(go env GOARCH)
+ chmod +x ./kind
+ sudo mv ./kind /usr/local/bin/kind
+
+ - name: Verify kind installation
+ run: kind version
+
+ - name: Running Test e2e
+ run: |
+ go mod tidy
+ make test-e2e
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
new file mode 100644
index 00000000000..fc2e80d304d
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+
+ - name: Running Tests
+ run: |
+ go mod tidy
+ make test
diff --git a/docs/book/src/getting-started/testdata/project/.gitignore b/docs/book/src/getting-started/testdata/project/.gitignore
new file mode 100644
index 00000000000..9f0f3a1c673
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.gitignore
@@ -0,0 +1,30 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+bin/*
+Dockerfile.cross
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Go workspace file
+go.work
+
+# Kubernetes Generated files - skip generated files, except for vendored files
+!vendor/**/zz_generated.*
+
+# editor and IDE paraphernalia
+.idea
+.vscode
+*.swp
+*.swo
+*~
+
+# Kubeconfig might contain secrets
+*.kubeconfig
diff --git a/docs/book/src/getting-started/testdata/project/.golangci.yml b/docs/book/src/getting-started/testdata/project/.golangci.yml
new file mode 100644
index 00000000000..e5b21b0f11c
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/.golangci.yml
@@ -0,0 +1,52 @@
+version: "2"
+run:
+ allow-parallel-runners: true
+linters:
+ default: none
+ enable:
+ - copyloopvar
+ - dupl
+ - errcheck
+ - ginkgolinter
+ - goconst
+ - gocyclo
+ - govet
+ - ineffassign
+ - lll
+ - misspell
+ - nakedret
+ - prealloc
+ - revive
+ - staticcheck
+ - unconvert
+ - unparam
+ - unused
+ settings:
+ revive:
+ rules:
+ - name: comment-spacings
+ - name: import-shadowing
+ exclusions:
+ generated: lax
+ rules:
+ - linters:
+ - lll
+ path: api/*
+ - linters:
+ - dupl
+ - lll
+ path: internal/*
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
+formatters:
+ enable:
+ - gofmt
+ - goimports
+ exclusions:
+ generated: lax
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
diff --git a/docs/book/src/getting-started/testdata/project/Dockerfile b/docs/book/src/getting-started/testdata/project/Dockerfile
new file mode 100644
index 00000000000..6466c484903
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/Dockerfile
@@ -0,0 +1,31 @@
+# Build the manager binary
+FROM golang:1.24 AS builder
+ARG TARGETOS
+ARG TARGETARCH
+
+WORKDIR /workspace
+# Copy the Go Modules manifests
+COPY go.mod go.mod
+COPY go.sum go.sum
+# cache deps before building and copying source so that we don't need to re-download as much
+# and so that source changes don't invalidate our downloaded layer
+RUN go mod download
+
+# Copy the Go source (relies on .dockerignore to filter)
+COPY . .
+
+# Build
+# the GOARCH has no default value to allow the binary to be built according to the host where the command
+# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
+# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
+# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
+
+# Use distroless as minimal base image to package the manager binary
+# Refer to https://github.com/GoogleContainerTools/distroless for more details
+FROM gcr.io/distroless/static:nonroot
+WORKDIR /
+COPY --from=builder /workspace/manager .
+USER 65532:65532
+
+ENTRYPOINT ["/manager"]
diff --git a/docs/book/src/getting-started/testdata/project/Makefile b/docs/book/src/getting-started/testdata/project/Makefile
new file mode 100644
index 00000000000..b964b4c8de8
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/Makefile
@@ -0,0 +1,250 @@
+# Image URL to use all building/pushing image targets
+IMG ?= controller:latest
+
+# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
+ifeq (,$(shell go env GOBIN))
+GOBIN=$(shell go env GOPATH)/bin
+else
+GOBIN=$(shell go env GOBIN)
+endif
+
+# CONTAINER_TOOL defines the container tool to be used for building images.
+# Be aware that the target commands are only tested with Docker which is
+# scaffolded by default. However, you might want to replace it to use other
+# tools. (i.e. podman)
+CONTAINER_TOOL ?= docker
+
+# Setting SHELL to bash allows bash commands to be executed by recipes.
+# Options are set to exit when a recipe line exits non-zero or a piped command fails.
+SHELL = /usr/bin/env bash -o pipefail
+.SHELLFLAGS = -ec
+
+.PHONY: all
+all: build
+
+##@ General
+
+# The help target prints out all targets with their descriptions organized
+# beneath their categories. The categories are represented by '##@' and the
+# target descriptions by '##'. The awk command is responsible for reading the
+# entire set of makefiles included in this invocation, looking for lines of the
+# file as xyz: ## something, and then pretty-format the target and help. Then,
+# if there's a line with ##@ something, that gets pretty-printed as a category.
+# More info on the usage of ANSI control characters for terminal formatting:
+# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+# More info on the awk command:
+# http://linuxcommand.org/lc3_adv_awk.php
+
+.PHONY: help
+help: ## Display this help.
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+##@ Development
+
+.PHONY: manifests
+manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
+ "$(CONTROLLER_GEN)" rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+
+.PHONY: generate
+generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
+ "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt" paths="./..."
+
+.PHONY: fmt
+fmt: ## Run go fmt against code.
+ go fmt ./...
+
+.PHONY: vet
+vet: ## Run go vet against code.
+ go vet ./...
+
+.PHONY: test
+test: manifests generate fmt vet setup-envtest ## Run tests.
+ KUBEBUILDER_ASSETS="$(shell "$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
+
+# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
+# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
+# CertManager is installed by default; skip with:
+# - CERT_MANAGER_INSTALL_SKIP=true
+KIND_CLUSTER ?= project-test-e2e
+
+.PHONY: setup-test-e2e
+setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
+ @command -v $(KIND) >/dev/null 2>&1 || { \
+ echo "Kind is not installed. Please install Kind manually."; \
+ exit 1; \
+ }
+ @case "$$($(KIND) get clusters)" in \
+ *"$(KIND_CLUSTER)"*) \
+ echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
+ *) \
+ echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
+ $(KIND) create cluster --name $(KIND_CLUSTER) ;; \
+ esac
+
+.PHONY: test-e2e
+test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
+ KIND=$(KIND) KIND_CLUSTER=$(KIND_CLUSTER) go test -tags=e2e ./test/e2e/ -v -ginkgo.v
+ $(MAKE) cleanup-test-e2e
+
+.PHONY: cleanup-test-e2e
+cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
+ @$(KIND) delete cluster --name $(KIND_CLUSTER)
+
+.PHONY: lint
+lint: golangci-lint ## Run golangci-lint linter
+ "$(GOLANGCI_LINT)" run
+
+.PHONY: lint-fix
+lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
+ "$(GOLANGCI_LINT)" run --fix
+
+.PHONY: lint-config
+lint-config: golangci-lint ## Verify golangci-lint linter configuration
+ "$(GOLANGCI_LINT)" config verify
+
+##@ Build
+
+.PHONY: build
+build: manifests generate fmt vet ## Build manager binary.
+ go build -o bin/manager cmd/main.go
+
+.PHONY: run
+run: manifests generate fmt vet ## Run a controller from your host.
+ go run ./cmd/main.go
+
+# If you wish to build the manager image targeting other platforms you can use the --platform flag.
+# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
+# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+.PHONY: docker-build
+docker-build: ## Build docker image with the manager.
+ $(CONTAINER_TOOL) build -t ${IMG} .
+
+.PHONY: docker-push
+docker-push: ## Push docker image with the manager.
+ $(CONTAINER_TOOL) push ${IMG}
+
+# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
+# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
+# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
+# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail)
+# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
+PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
+.PHONY: docker-buildx
+docker-buildx: ## Build and push docker image for the manager for cross-platform support
+ # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
+ sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
+ - $(CONTAINER_TOOL) buildx create --name project-builder
+ $(CONTAINER_TOOL) buildx use project-builder
+ - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
+ - $(CONTAINER_TOOL) buildx rm project-builder
+ rm Dockerfile.cross
+
+.PHONY: build-installer
+build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
+ mkdir -p dist
+ cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG}
+ "$(KUSTOMIZE)" build config/default > dist/install.yaml
+
+##@ Deployment
+
+ifndef ignore-not-found
+ ignore-not-found = false
+endif
+
+.PHONY: install
+install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
+ @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
+ if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" apply -f -; else echo "No CRDs to install; skipping."; fi
+
+.PHONY: uninstall
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+ @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
+ if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -; else echo "No CRDs to delete; skipping."; fi
+
+.PHONY: deploy
+deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
+ cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG}
+ "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" apply -f -
+
+.PHONY: undeploy
+undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+ "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -
+
+##@ Dependencies
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+ mkdir -p "$(LOCALBIN)"
+
+## Tool Binaries
+KUBECTL ?= kubectl
+KIND ?= kind
+KUSTOMIZE ?= $(LOCALBIN)/kustomize
+CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
+
+## Tool Versions
+KUSTOMIZE_VERSION ?= v5.7.1
+CONTROLLER_TOOLS_VERSION ?= v0.19.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 v='$(call gomodver,sigs.k8s.io/controller-runtime)'; \
+ [ -n "$$v" ] || { echo "Set ENVTEST_VERSION manually (controller-runtime replace has no tag)" >&2; exit 1; }; \
+ printf '%s\n' "$$v" | sed -E 's/^v?([0-9]+)\.([0-9]+).*/release-\1.\2/')
+
+#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
+ENVTEST_K8S_VERSION ?= $(shell v='$(call gomodver,k8s.io/api)'; \
+ [ -n "$$v" ] || { echo "Set ENVTEST_K8S_VERSION manually (k8s.io/api replace has no tag)" >&2; exit 1; }; \
+ printf '%s\n' "$$v" | sed -E 's/^v?[0-9]+\.([0-9]+).*/1.\1/')
+
+GOLANGCI_LINT_VERSION ?= v2.6.0
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
+$(KUSTOMIZE): $(LOCALBIN)
+ $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
+
+.PHONY: controller-gen
+controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
+$(CONTROLLER_GEN): $(LOCALBIN)
+ $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
+
+.PHONY: setup-envtest
+setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
+ @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..."
+ @"$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path || { \
+ echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \
+ exit 1; \
+ }
+
+.PHONY: envtest
+envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
+$(ENVTEST): $(LOCALBIN)
+ $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
+
+.PHONY: golangci-lint
+golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
+$(GOLANGCI_LINT): $(LOCALBIN)
+ $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
+
+# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
+# $1 - target path with name of binary
+# $2 - package url which can be installed
+# $3 - specific version of package
+define go-install-tool
+@[ -f "$(1)-$(3)" ] && [ "$$(readlink -- "$(1)" 2>/dev/null)" = "$(1)-$(3)" ] || { \
+set -e; \
+package=$(2)@$(3) ;\
+echo "Downloading $${package}" ;\
+rm -f "$(1)" ;\
+GOBIN="$(LOCALBIN)" go install $${package} ;\
+mv "$(LOCALBIN)/$$(basename "$(1)")" "$(1)-$(3)" ;\
+} ;\
+ln -sf "$$(realpath "$(1)-$(3)")" "$(1)"
+endef
+
+define gomodver
+$(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' $(1) 2>/dev/null)
+endef
diff --git a/docs/book/src/getting-started/testdata/project/PROJECT b/docs/book/src/getting-started/testdata/project/PROJECT
new file mode 100644
index 00000000000..64dea029144
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/PROJECT
@@ -0,0 +1,26 @@
+# Code generated by tool. DO NOT EDIT.
+# This file is used to track the info used to scaffold your project
+# and allow the plugins properly work.
+# More info: https://book.kubebuilder.io/reference/project-config.html
+cliVersion: (devel)
+domain: example.com
+layout:
+- go.kubebuilder.io/v4
+plugins:
+ autoupdate.kubebuilder.io/v1-alpha: {}
+ helm.kubebuilder.io/v2-alpha:
+ manifests: dist/install.yaml
+ output: dist
+projectName: project
+repo: example.com/memcached
+resources:
+- api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: example.com
+ group: cache
+ kind: Memcached
+ path: example.com/memcached/api/v1alpha1
+ version: v1alpha1
+version: "3"
diff --git a/docs/book/src/getting-started/testdata/project/README.md b/docs/book/src/getting-started/testdata/project/README.md
new file mode 100644
index 00000000000..fcc53b94123
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/README.md
@@ -0,0 +1,135 @@
+# project
+// TODO(user): Add simple overview of use/purpose
+
+## Description
+// TODO(user): An in-depth paragraph about your project and overview of use
+
+## Getting Started
+
+### Prerequisites
+- go version v1.24.6+
+- docker version 17.03+.
+- kubectl version v1.11.3+.
+- Access to a Kubernetes v1.11.3+ cluster.
+
+### To Deploy on the cluster
+**Build and push your image to the location specified by `IMG`:**
+
+```sh
+make docker-build docker-push IMG=/project:tag
+```
+
+**NOTE:** This image ought to be published in the personal registry you specified.
+And it is required to have access to pull the image from the working environment.
+Make sure you have the proper permission to the registry if the above commands don’t work.
+
+**Install the CRDs into the cluster:**
+
+```sh
+make install
+```
+
+**Deploy the Manager to the cluster with the image specified by `IMG`:**
+
+```sh
+make deploy IMG=/project:tag
+```
+
+> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin
+privileges or be logged in as admin.
+
+**Create instances of your solution**
+You can apply the samples (examples) from the config/sample:
+
+```sh
+kubectl apply -k config/samples/
+```
+
+>**NOTE**: Ensure that the samples has default values to test it out.
+
+### To Uninstall
+**Delete the instances (CRs) from the cluster:**
+
+```sh
+kubectl delete -k config/samples/
+```
+
+**Delete the APIs(CRDs) from the cluster:**
+
+```sh
+make uninstall
+```
+
+**UnDeploy the controller from the cluster:**
+
+```sh
+make undeploy
+```
+
+## Project Distribution
+
+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:
+
+```sh
+make build-installer IMG=/project:tag
+```
+
+**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.
+
+2. Using the installer
+
+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/v2-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
+
+**NOTE:** Run `make help` for more information on all potential `make` targets
+
+More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
+
+## License
+
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under 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
new file mode 100644
index 00000000000..e4fa7421e88
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/api/v1alpha1/groupversion_info.go
@@ -0,0 +1,36 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package v1alpha1 contains API Schema definitions for the cache v1alpha1 API group.
+// +kubebuilder:object:generate=true
+// +groupName=cache.example.com
+package v1alpha1
+
+import (
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "sigs.k8s.io/controller-runtime/pkg/scheme"
+)
+
+var (
+ // GroupVersion is group version used to register these objects.
+ GroupVersion = schema.GroupVersion{Group: "cache.example.com", Version: "v1alpha1"}
+
+ // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
+ SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+
+ // AddToScheme adds the types in this group-version to the given scheme.
+ AddToScheme = SchemeBuilder.AddToScheme
+)
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
new file mode 100644
index 00000000000..fc9e4712206
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/api/v1alpha1/memcached_types.go
@@ -0,0 +1,100 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
+// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+// MemcachedSpec defines the desired state of Memcached
+type MemcachedSpec struct {
+ // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+ // Important: Run "make" to regenerate code after modifying this file
+ // The following markers will use OpenAPI v3 schema to validate the value
+ // More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
+
+ // size defines the number of Memcached instances
+ // The following markers will use OpenAPI v3 schema to validate the value
+ // More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
+ // +kubebuilder:validation:Minimum=1
+ // +kubebuilder:validation:Maximum=3
+ // +kubebuilder:validation:ExclusiveMaximum=false
+ // +optional
+ Size *int32 `json:"size,omitempty"`
+}
+
+// MemcachedStatus defines the observed state of Memcached.
+type MemcachedStatus struct {
+ // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+ // Important: Run "make" to regenerate code after modifying this file
+
+ // For Kubernetes API conventions, see:
+ // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
+
+ // conditions represent the current state of the Memcached resource.
+ // Each condition has a unique type and reflects the status of a specific aspect of the resource.
+ //
+ // Standard condition types include:
+ // - "Available": the resource is fully functional
+ // - "Progressing": the resource is being created or updated
+ // - "Degraded": the resource failed to reach or maintain its desired state
+ //
+ // The status of each condition is one of True, False, or Unknown.
+ // +listType=map
+ // +listMapKey=type
+ // +optional
+ Conditions []metav1.Condition `json:"conditions,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// Memcached is the Schema for the memcacheds API
+type Memcached struct {
+ metav1.TypeMeta `json:",inline"`
+
+ // metadata is a standard object metadata
+ // +optional
+ metav1.ObjectMeta `json:"metadata,omitzero"`
+
+ // spec defines the desired state of Memcached
+ // +required
+ Spec MemcachedSpec `json:"spec"`
+
+ // status defines the observed state of Memcached
+ // +optional
+ Status MemcachedStatus `json:"status,omitzero"`
+}
+
+// +kubebuilder:object:root=true
+
+// MemcachedList contains a list of Memcached
+type MemcachedList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitzero"`
+ Items []Memcached `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&Memcached{}, &MemcachedList{})
+}
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
new file mode 100644
index 00000000000..9e9bd0cae7d
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/api/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,127 @@
+//go:build !ignore_autogenerated
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by controller-gen. DO NOT EDIT.
+
+package v1alpha1
+
+import (
+ "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Memcached) DeepCopyInto(out *Memcached) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+ in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Memcached.
+func (in *Memcached) DeepCopy() *Memcached {
+ if in == nil {
+ return nil
+ }
+ out := new(Memcached)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Memcached) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MemcachedList) DeepCopyInto(out *MemcachedList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]Memcached, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemcachedList.
+func (in *MemcachedList) DeepCopy() *MemcachedList {
+ if in == nil {
+ return nil
+ }
+ out := new(MemcachedList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *MemcachedList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MemcachedSpec) DeepCopyInto(out *MemcachedSpec) {
+ *out = *in
+ if in.Size != nil {
+ in, out := &in.Size, &out.Size
+ *out = new(int32)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemcachedSpec.
+func (in *MemcachedSpec) DeepCopy() *MemcachedSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(MemcachedSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MemcachedStatus) DeepCopyInto(out *MemcachedStatus) {
+ *out = *in
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]v1.Condition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MemcachedStatus.
+func (in *MemcachedStatus) DeepCopy() *MemcachedStatus {
+ if in == nil {
+ return nil
+ }
+ out := new(MemcachedStatus)
+ in.DeepCopyInto(out)
+ return out
+}
diff --git a/docs/book/src/getting-started/testdata/project/cmd/main.go b/docs/book/src/getting-started/testdata/project/cmd/main.go
new file mode 100644
index 00000000000..020b7664abc
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/cmd/main.go
@@ -0,0 +1,204 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "crypto/tls"
+ "flag"
+ "os"
+
+ // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
+ // to ensure that exec-entrypoint and run can make use of them.
+ _ "k8s.io/client-go/plugin/pkg/client/auth"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ 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/healthz"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/filters"
+ metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+
+ cachev1alpha1 "example.com/memcached/api/v1alpha1"
+ "example.com/memcached/internal/controller"
+ // +kubebuilder:scaffold:imports
+)
+
+var (
+ scheme = runtime.NewScheme()
+ setupLog = ctrl.Log.WithName("setup")
+)
+
+func init() {
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+
+ utilruntime.Must(cachev1alpha1.AddToScheme(scheme))
+ // +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
+ var enableHTTP2 bool
+ var tlsOpts []func(*tls.Config)
+ flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
+ flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
+ flag.BoolVar(&enableLeaderElection, "leader-elect", false,
+ "Enable leader election for controller manager. "+
+ "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{
+ Development: true,
+ }
+ opts.BindFlags(flag.CommandLine)
+ flag.Parse()
+
+ ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+
+ // if the enable-http2 flag is false (the default), http/2 should be disabled
+ // due to its vulnerabilities. More specifically, disabling http/2 will
+ // prevent from being vulnerable to the HTTP/2 Stream Cancellation and
+ // Rapid Reset CVEs. For more information see:
+ // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
+ // - https://github.com/advisories/GHSA-4374-p667-p6c8
+ disableHTTP2 := func(c *tls.Config) {
+ setupLog.Info("disabling http/2")
+ c.NextProtos = []string{"http/1.1"}
+ }
+
+ if !enableHTTP2 {
+ tlsOpts = append(tlsOpts, disableHTTP2)
+ }
+
+ // Initial webhook TLS options
+ webhookTLSOpts := tlsOpts
+ webhookServerOptions := webhook.Options{
+ TLSOpts: webhookTLSOpts,
+ }
+
+ 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)
+
+ webhookServerOptions.CertDir = webhookCertPath
+ webhookServerOptions.CertName = webhookCertName
+ webhookServerOptions.KeyName = webhookCertKey
+ }
+
+ webhookServer := webhook.NewServer(webhookServerOptions)
+
+ // 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.22.4/pkg/metrics/server
+ // - https://book.kubebuilder.io/reference/metrics.html
+ metricsServerOptions := metricsserver.Options{
+ BindAddress: metricsAddr,
+ SecureServing: secureMetrics,
+ TLSOpts: tlsOpts,
+ }
+
+ if secureMetrics {
+ // 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.22.4/pkg/metrics/filters#WithAuthenticationAndAuthorization
+ metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
+ }
+
+ // 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)
+
+ metricsServerOptions.CertDir = metricsCertPath
+ metricsServerOptions.CertName = metricsCertName
+ metricsServerOptions.KeyName = metricsCertKey
+ }
+
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ Metrics: metricsServerOptions,
+ WebhookServer: webhookServer,
+ HealthProbeBindAddress: probeAddr,
+ LeaderElection: enableLeaderElection,
+ LeaderElectionID: "4b13cc52.example.com",
+ // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
+ // when the Manager ends. This requires the binary to immediately end when the
+ // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
+ // speeds up voluntary leader transitions as the new leader don't have to wait
+ // LeaseDuration time first.
+ //
+ // In the default scaffold provided, the program ends immediately after
+ // the manager stops, so would be fine to enable this option. However,
+ // if you are doing or is intended to do any operation such as perform cleanups
+ // after the manager stops then its usage might be unsafe.
+ // LeaderElectionReleaseOnCancel: true,
+ })
+ if err != nil {
+ setupLog.Error(err, "unable to start manager")
+ os.Exit(1)
+ }
+
+ if err := (&controller.MemcachedReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "Memcached")
+ os.Exit(1)
+ }
+ // +kubebuilder:scaffold:builder
+
+ if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up health check")
+ os.Exit(1)
+ }
+ if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up ready check")
+ os.Exit(1)
+ }
+
+ setupLog.Info("starting manager")
+ if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
+ setupLog.Error(err, "problem running manager")
+ 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
new file mode 100644
index 00000000000..25a6dfbafe3
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/crd/bases/cache.example.com_memcacheds.yaml
@@ -0,0 +1,131 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: memcacheds.cache.example.com
+spec:
+ group: cache.example.com
+ names:
+ kind: Memcached
+ listKind: MemcachedList
+ plural: memcacheds
+ singular: memcached
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: Memcached is the Schema for the memcacheds API
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: spec defines the desired state of Memcached
+ properties:
+ size:
+ description: |-
+ size defines the number of Memcached instances
+ The following markers will use OpenAPI v3 schema to validate the value
+ More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
+ format: int32
+ maximum: 3
+ minimum: 1
+ type: integer
+ type: object
+ status:
+ description: status defines the observed state of Memcached
+ properties:
+ conditions:
+ description: |-
+ conditions represent the current state of the Memcached resource.
+ Each condition has a unique type and reflects the status of a specific aspect of the resource.
+
+ Standard condition types include:
+ - "Available": the resource is fully functional
+ - "Progressing": the resource is being created or updated
+ - "Degraded": the resource failed to reach or maintain its desired state
+
+ The status of each condition is one of True, False, or Unknown.
+ items:
+ description: Condition contains details for one aspect of the current
+ state of this API Resource.
+ properties:
+ lastTransitionTime:
+ description: |-
+ lastTransitionTime is the last time the condition transitioned from one status to another.
+ This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: |-
+ message is a human readable message indicating details about the transition.
+ This may be an empty string.
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ description: |-
+ observedGeneration represents the .metadata.generation that the condition was set based upon.
+ For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+ with respect to the current state of the instance.
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ description: |-
+ reason contains a programmatic identifier indicating the reason for the condition's last transition.
+ Producers of specific condition types may define expected values and meanings for this field,
+ and whether the values are considered a guaranteed API.
+ The value should be a CamelCase string.
+ This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: status of the condition, one of True, False, Unknown.
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ description: type of condition in CamelCase or in foo.example.com/CamelCase.
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
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
new file mode 100644
index 00000000000..bdf76e3b9ca
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/crd/kustomization.yaml
@@ -0,0 +1,16 @@
+# This kustomization.yaml is not intended to be run by itself,
+# since it depends on service name and namespace that are out of this kustomize package.
+# It should be run by config/default
+resources:
+- bases/cache.example.com_memcacheds.yaml
+# +kubebuilder:scaffold:crdkustomizeresource
+
+patches:
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
+# patches here are for enabling the conversion webhook for each CRD
+# +kubebuilder:scaffold:crdkustomizewebhookpatch
+
+# [WEBHOOK] To enable webhook, uncomment the following section
+# the following config is for teaching kustomize how to do kustomization for CRDs.
+#configurations:
+#- kustomizeconfig.yaml
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/crd/kustomizeconfig.yaml b/docs/book/src/getting-started/testdata/project/config/crd/kustomizeconfig.yaml
similarity index 100%
rename from docs/book/src/component-config-tutorial/testdata/project/config/crd/kustomizeconfig.yaml
rename to docs/book/src/getting-started/testdata/project/config/crd/kustomizeconfig.yaml
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
new file mode 100644
index 00000000000..02fa7e87c35
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/default/kustomization.yaml
@@ -0,0 +1,234 @@
+# Adds namespace to all resources.
+namespace: project-system
+
+# Value of this field is prepended to the
+# names of all resources, e.g. a deployment named
+# "wordpress" becomes "alices-wordpress".
+# Note that it should also match with the prefix (text before '-') of the namespace
+# field above.
+namePrefix: project-
+
+# Labels to add to all resources and selectors.
+#labels:
+#- includeSelectors: true
+# pairs:
+# someName: someValue
+
+resources:
+- ../crd
+- ../rbac
+- ../manager
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
+# crd/kustomization.yaml
+#- ../webhook
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
+#- ../certmanager
+# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
+#- ../prometheus
+# [METRICS] Expose the controller manager metrics service.
+- metrics_service.yaml
+# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
+# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
+# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will
+# be able to communicate with the Webhook Server.
+#- ../network-policy
+
+# 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
+- path: manager_metrics_patch.yaml
+ 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
+# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor
+# kind: ServiceMonitor
+# group: monitoring.coreos.com
+# version: v1
+# name: controller-manager-metrics-monitor
+# fieldPaths:
+# - spec.endpoints.0.tlsConfig.serverName
+# 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
+# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor
+# kind: ServiceMonitor
+# group: monitoring.coreos.com
+# version: v1
+# name: controller-manager-metrics-monitor
+# fieldPaths:
+# - spec.endpoints.0.tlsConfig.serverName
+# options:
+# delimiter: '.'
+# index: 1
+# create: true
+
+# - source: # Uncomment the following block if you have any webhook
+# kind: Service
+# version: v1
+# name: webhook-service
+# fieldPath: .metadata.name # Name of the service
+# targets:
+# - select:
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPaths:
+# - .spec.dnsNames.0
+# - .spec.dnsNames.1
+# options:
+# delimiter: '.'
+# index: 0
+# create: true
+# - source:
+# kind: Service
+# version: v1
+# name: webhook-service
+# fieldPath: .metadata.namespace # Namespace of the service
+# targets:
+# - select:
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPaths:
+# - .spec.dnsNames.0
+# - .spec.dnsNames.1
+# options:
+# delimiter: '.'
+# index: 1
+# create: true
+
+# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation)
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert # This name should match the one in certificate.yaml
+# fieldPath: .metadata.namespace # Namespace of the certificate CR
+# targets:
+# - select:
+# kind: ValidatingWebhookConfiguration
+# fieldPaths:
+# - .metadata.annotations.[cert-manager.io/inject-ca-from]
+# options:
+# delimiter: '/'
+# index: 0
+# create: true
+# - source:
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPath: .metadata.name
+# targets:
+# - select:
+# kind: ValidatingWebhookConfiguration
+# fieldPaths:
+# - .metadata.annotations.[cert-manager.io/inject-ca-from]
+# options:
+# delimiter: '/'
+# index: 1
+# create: true
+
+# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting )
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPath: .metadata.namespace # Namespace of the certificate CR
+# targets:
+# - select:
+# kind: MutatingWebhookConfiguration
+# fieldPaths:
+# - .metadata.annotations.[cert-manager.io/inject-ca-from]
+# options:
+# delimiter: '/'
+# index: 0
+# create: true
+# - source:
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPath: .metadata.name
+# targets:
+# - select:
+# kind: MutatingWebhookConfiguration
+# fieldPaths:
+# - .metadata.annotations.[cert-manager.io/inject-ca-from]
+# options:
+# delimiter: '/'
+# index: 1
+# create: true
+
+# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
+# kind: Certificate
+# group: cert-manager.io
+# version: v1
+# name: serving-cert
+# fieldPath: .metadata.namespace # Namespace of the certificate CR
+# 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
+# fieldPath: .metadata.name
+# 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/manager_metrics_patch.yaml b/docs/book/src/getting-started/testdata/project/config/default/manager_metrics_patch.yaml
new file mode 100644
index 00000000000..2aaef6536f4
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/default/manager_metrics_patch.yaml
@@ -0,0 +1,4 @@
+# This patch adds the args to allow exposing the metrics endpoint using HTTPS
+- op: add
+ path: /spec/template/spec/containers/0/args/0
+ value: --metrics-bind-address=:8443
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
new file mode 100644
index 00000000000..df85a7387c5
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/default/metrics_service.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: controller-manager-metrics-service
+ namespace: system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: 8443
+ selector:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
diff --git a/docs/book/src/getting-started/testdata/project/config/manager/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/manager/kustomization.yaml
new file mode 100644
index 00000000000..ad13e96b3fc
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/manager/kustomization.yaml
@@ -0,0 +1,8 @@
+resources:
+- manager.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+images:
+- name: controller
+ newName: controller
+ newTag: latest
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
new file mode 100644
index 00000000000..8fb2249a918
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/manager/manager.yaml
@@ -0,0 +1,99 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: system
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: controller-manager
+ namespace: system
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+spec:
+ selector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ replicas: 1
+ template:
+ metadata:
+ annotations:
+ 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.
+ # It is considered best practice to support multiple architectures. You can
+ # build your manager image using the makefile target docker-buildx.
+ # affinity:
+ # nodeAffinity:
+ # requiredDuringSchedulingIgnoredDuringExecution:
+ # nodeSelectorTerms:
+ # - matchExpressions:
+ # - key: kubernetes.io/arch
+ # operator: In
+ # values:
+ # - amd64
+ # - arm64
+ # - ppc64le
+ # - s390x
+ # - key: kubernetes.io/os
+ # operator: In
+ # 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
+ seccompProfile:
+ type: RuntimeDefault
+ containers:
+ - command:
+ - /manager
+ args:
+ - --leader-elect
+ - --health-probe-bind-address=:8081
+ image: controller:latest
+ name: manager
+ ports: []
+ securityContext:
+ readOnlyRootFilesystem: true
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - "ALL"
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ # TODO(user): Configure the resources accordingly based on the project requirements.
+ # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ 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
new file mode 100644
index 00000000000..0e64d74ef6e
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/network-policy/allow-metrics-traffic.yaml
@@ -0,0 +1,27 @@
+# This NetworkPolicy allows ingress traffic
+# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
+# namespaces are able to gather data from the metrics endpoint.
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: allow-metrics-traffic
+ namespace: system
+spec:
+ podSelector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ policyTypes:
+ - Ingress
+ ingress:
+ # This allows ingress traffic from any namespace with the label metrics: enabled
+ - from:
+ - namespaceSelector:
+ matchLabels:
+ metrics: enabled # Only from namespaces with this label
+ ports:
+ - port: 8443
+ protocol: TCP
diff --git a/docs/book/src/getting-started/testdata/project/config/network-policy/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/network-policy/kustomization.yaml
new file mode 100644
index 00000000000..ec0fb5e57df
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/network-policy/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- allow-metrics-traffic.yaml
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
new file mode 100644
index 00000000000..fdc5481b103
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/prometheus/kustomization.yaml
@@ -0,0 +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
new file mode 100644
index 00000000000..3a0798c331e
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/prometheus/monitor.yaml
@@ -0,0 +1,27 @@
+# Prometheus Monitor Service (Metrics)
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: controller-manager-metrics-monitor
+ namespace: system
+spec:
+ endpoints:
+ - path: /metrics
+ port: https # Ensure this is the name of the port that exposes HTTPS metrics
+ scheme: https
+ 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, 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..5bf84ce0d53
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/prometheus/monitor_tls_patch.yaml
@@ -0,0 +1,19 @@
+# Patch for Prometheus ServiceMonitor to enable secure TLS configuration
+# using certificates managed by cert-manager
+- op: replace
+ path: /spec/endpoints/0/tlsConfig
+ value:
+ # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
+ serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
+ 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
new file mode 100644
index 00000000000..a5256ee4bf4
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/kustomization.yaml
@@ -0,0 +1,28 @@
+resources:
+# All RBAC will be applied under this service account in
+# the deployment namespace. You may comment out this resource
+# if your manager will use a service account that exists at
+# runtime. Be sure to update RoleBinding and ClusterRoleBinding
+# subjects if changing service account names.
+- service_account.yaml
+- role.yaml
+- role_binding.yaml
+- leader_election_role.yaml
+- leader_election_role_binding.yaml
+# The following RBAC configurations are used to protect
+# the metrics endpoint with authn/authz. These configurations
+# ensure that only authorized users and service accounts
+# can access the metrics endpoint. Comment the following
+# permissions if you want to disable this protection.
+# More info: https://book.kubebuilder.io/reference/metrics.html
+- metrics_auth_role.yaml
+- metrics_auth_role_binding.yaml
+- metrics_reader_role.yaml
+# 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
+# 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/leader_election_role.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/leader_election_role.yaml
new file mode 100644
index 00000000000..e3fc403c0d9
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/leader_election_role.yaml
@@ -0,0 +1,40 @@
+# permissions to do leader election.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: leader-election-role
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/leader_election_role_binding.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/leader_election_role_binding.yaml
new file mode 100644
index 00000000000..133026ff212
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/leader_election_role_binding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: leader-election-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: leader-election-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
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
new file mode 100644
index 00000000000..a3542f23263
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_editor_role.yaml
@@ -0,0 +1,33 @@
+# 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:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: memcached-editor-role
+rules:
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
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
new file mode 100644
index 00000000000..079cb76a538
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/memcached_viewer_role.yaml
@@ -0,0 +1,29 @@
+# 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:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: memcached-viewer-role
+rules:
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/metrics_auth_role.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/metrics_auth_role.yaml
new file mode 100644
index 00000000000..32d2e4ec6b0
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/metrics_auth_role.yaml
@@ -0,0 +1,17 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: metrics-auth-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/metrics_auth_role_binding.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/metrics_auth_role_binding.yaml
new file mode 100644
index 00000000000..e775d67ff08
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/metrics_auth_role_binding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: metrics-auth-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
diff --git a/testdata/project-v3-config/config/rbac/auth_proxy_client_clusterrole.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/metrics_reader_role.yaml
similarity index 100%
rename from testdata/project-v3-config/config/rbac/auth_proxy_client_clusterrole.yaml
rename to docs/book/src/getting-started/testdata/project/config/rbac/metrics_reader_role.yaml
diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/role.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/role.yaml
new file mode 100644
index 00000000000..5e37ce535fe
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/role.yaml
@@ -0,0 +1,59 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: manager-role
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
+ - patch
+ - update
diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/role_binding.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/role_binding.yaml
new file mode 100644
index 00000000000..1e81c2443c8
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/role_binding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: manager-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
diff --git a/docs/book/src/getting-started/testdata/project/config/rbac/service_account.yaml b/docs/book/src/getting-started/testdata/project/config/rbac/service_account.yaml
new file mode 100644
index 00000000000..7733e7fc663
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/rbac/service_account.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: controller-manager
+ namespace: system
diff --git a/docs/book/src/getting-started/testdata/project/config/samples/cache_v1alpha1_memcached.yaml b/docs/book/src/getting-started/testdata/project/config/samples/cache_v1alpha1_memcached.yaml
new file mode 100644
index 00000000000..26614892b46
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/samples/cache_v1alpha1_memcached.yaml
@@ -0,0 +1,11 @@
+apiVersion: cache.example.com/v1alpha1
+kind: Memcached
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: memcached-sample
+spec:
+ # TODO(user): edit the following value to ensure the number
+ # of Pods/Instances your Operand must have on cluster
+ size: 1
diff --git a/docs/book/src/getting-started/testdata/project/config/samples/kustomization.yaml b/docs/book/src/getting-started/testdata/project/config/samples/kustomization.yaml
new file mode 100644
index 00000000000..44dac6f8786
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/config/samples/kustomization.yaml
@@ -0,0 +1,4 @@
+## Append samples of your project ##
+resources:
+- cache_v1alpha1_memcached.yaml
+# +kubebuilder:scaffold:manifestskustomizesamples
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/.helmignore b/docs/book/src/getting-started/testdata/project/dist/chart/.helmignore
new file mode 100644
index 00000000000..7d92f7fb4f1
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/.helmignore
@@ -0,0 +1,25 @@
+# Patterns to ignore when building Helm packages.
+# Operating system files
+.DS_Store
+
+# Version control directories
+.git/
+.gitignore
+.bzr/
+.hg/
+.hgignore
+.svn/
+
+# Backup and temporary files
+*.swp
+*.tmp
+*.bak
+*.orig
+*~
+
+# IDE and editor-related files
+.idea/
+.vscode/
+
+# Helm chart artifacts
+dist/chart/*.tgz
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/Chart.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/Chart.yaml
new file mode 100644
index 00000000000..4781b2de9dd
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: project
+description: A Helm chart to distribute the project project
+type: application
+version: 0.1.0
+appVersion: "0.1.0"
+icon: "https://example.com/icon.png"
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/_helpers.tpl b/docs/book/src/getting-started/testdata/project/dist/chart/templates/_helpers.tpl
new file mode 100644
index 00000000000..5aced50daec
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/_helpers.tpl
@@ -0,0 +1,78 @@
+{{/*
+Chart name based on project name.
+Truncated to 63 characters for Kubernetes compatibility.
+*/}}
+{{- define "chart.name" -}}
+{{- if .Chart }}
+ {{- if .Chart.Name }}
+ {{- .Chart.Name | trunc 63 | trimSuffix "-" }}
+ {{- else }}
+ project
+ {{- end }}
+{{- else }}
+ project
+{{- end }}
+{{- end }}
+
+{{/*
+Full name of the chart (with release name prefix).
+Combines release name with chart name.
+Truncated to 63 characters for Kubernetes compatibility.
+*/}}
+{{- define "chart.fullname" -}}
+{{- $name := include "chart.name" . }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+
+{{/*
+Namespace for generated references.
+Always uses the Helm release namespace.
+*/}}
+{{- define "chart.namespaceName" -}}
+{{ .Release.Namespace }}
+{{- end }}
+
+
+
+{{/*
+Service name with proper truncation for Kubernetes 63-character limit.
+Takes a context with .suffix for the service type (e.g., "webhook-service").
+If fullname + suffix exceeds 63 chars, truncates fullname to 45 chars.
+*/}}
+{{- define "chart.serviceName" -}}
+{{- $fullname := include "chart.fullname" .context -}}
+{{- if gt (len $fullname) 45 -}}
+{{- printf "%s-%s" (trunc 45 $fullname | trimSuffix "-") .suffix | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" $fullname .suffix | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end }}
+
+{{/*
+Common labels for Helm charts.
+Includes app version, chart version, app name, instance, and managed-by labels.
+*/}}
+{{- define "chart.labels" -}}
+{{- if .Chart.AppVersion -}}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+{{- if .Chart.Version }}
+helm.sh/chart: {{ .Chart.Version | quote }}
+{{- end }}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels for matching pods and services.
+Only includes name and instance for consistent selection.
+*/}}
+{{- define "chart.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/crd/memcacheds.cache.example.com.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/crd/memcacheds.cache.example.com.yaml
new file mode 100644
index 00000000000..f25cd75ab51
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/crd/memcacheds.cache.example.com.yaml
@@ -0,0 +1,131 @@
+{{- if .Values.crd.enable }}
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: memcacheds.cache.example.com
+spec:
+ group: cache.example.com
+ names:
+ kind: Memcached
+ listKind: MemcachedList
+ plural: memcacheds
+ singular: memcached
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: Memcached is the Schema for the memcacheds API
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: spec defines the desired state of Memcached
+ properties:
+ size:
+ description: |-
+ size defines the number of Memcached instances
+ The following markers will use OpenAPI v3 schema to validate the value
+ More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
+ format: int32
+ maximum: 3
+ minimum: 1
+ type: integer
+ type: object
+ status:
+ description: status defines the observed state of Memcached
+ properties:
+ conditions:
+ description: |-
+ conditions represent the current state of the Memcached resource.
+ Each condition has a unique type and reflects the status of a specific aspect of the resource.
+
+ Standard condition types include:
+ - "Available": the resource is fully functional
+ - "Progressing": the resource is being created or updated
+ - "Degraded": the resource failed to reach or maintain its desired state
+
+ The status of each condition is one of True, False, or Unknown.
+ items:
+ description: Condition contains details for one aspect of the current state of this API Resource.
+ properties:
+ lastTransitionTime:
+ description: |-
+ lastTransitionTime is the last time the condition transitioned from one status to another.
+ This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: |-
+ message is a human readable message indicating details about the transition.
+ This may be an empty string.
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ description: |-
+ observedGeneration represents the .metadata.generation that the condition was set based upon.
+ For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+ with respect to the current state of the instance.
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ description: |-
+ reason contains a programmatic identifier indicating the reason for the condition's last transition.
+ Producers of specific condition types may define expected values and meanings for this field,
+ and whether the values are considered a guaranteed API.
+ The value should be a CamelCase string.
+ This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: status of the condition, one of True, False, Unknown.
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ description: type of condition in CamelCase or in foo.example.com/CamelCase.
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/manager/manager.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/manager/manager.yaml
new file mode 100644
index 00000000000..8d4e7514bae
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/manager/manager.yaml
@@ -0,0 +1,75 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
+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:
+ - args:
+ {{- if .Values.metrics.enable }}
+ - --metrics-bind-address=:{{ .Values.metrics.port }}
+ {{- else }}
+ # Bind to :0 to disable the controller-runtime managed metrics server
+ - --metrics-bind-address=0
+ {{- end }}
+ - --health-probe-bind-address=:8081
+ {{- range .Values.manager.args }}
+ - {{ . }}
+ {{- end }}
+ command:
+ - /manager
+ image: "{{ .Values.manager.image.repository }}:{{ .Values.manager.image.tag }}"
+ imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ ports: []
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ {{- if .Values.manager.resources }}
+ {{- toYaml .Values.manager.resources | nindent 20 }}
+ {{- else }}
+ {}
+ {{- end }}
+ securityContext:
+ {{- if .Values.manager.securityContext }}
+ {{- toYaml .Values.manager.securityContext | nindent 20 }}
+ {{- else }}
+ {}
+ {{- end }}
+ volumeMounts: []
+ securityContext:
+ {{- if .Values.manager.podSecurityContext }}
+ {{- toYaml .Values.manager.podSecurityContext | nindent 14 }}
+ {{- else }}
+ {}
+ {{- end }}
+ serviceAccountName: project-controller-manager
+ terminationGracePeriodSeconds: 10
+ volumes: []
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml
new file mode 100644
index 00000000000..c5b24a9125a
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.metrics.enable }}
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-service
+ namespace: {{ .Release.Namespace }}
+spec:
+ ports:
+ - name: https
+ port: {{ .Values.metrics.port }}
+ protocol: TCP
+ targetPort: {{ .Values.metrics.port }}
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/monitoring/servicemonitor.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/monitoring/servicemonitor.yaml
new file mode 100644
index 00000000000..35d515eac00
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/monitoring/servicemonitor.yaml
@@ -0,0 +1,39 @@
+{{- if .Values.prometheus.enable }}
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+ labels:
+ {{- include "chart.labels" . | nindent 4 }}
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-monitor
+ namespace: {{ .Release.Namespace }}
+spec:
+ endpoints:
+ - path: /metrics
+ port: https
+ scheme: https
+ bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+ tlsConfig:
+ {{- if .Values.certManager.enable }}
+ serverName: project-controller-manager-metrics-service.{{ .Release.Namespace }}.svc
+ # Apply secure TLS configuration with cert-manager
+ insecureSkipVerify: false
+ ca:
+ secret:
+ name: metrics-server-cert
+ key: ca.crt
+ cert:
+ secret:
+ name: metrics-server-cert
+ key: tls.crt
+ keySecret:
+ name: metrics-server-cert
+ key: tls.key
+ {{- else }}
+ # Development/Test mode (insecure configuration)
+ insecureSkipVerify: true
+ {{- end }}
+ selector:
+ matchLabels:
+ control-plane: controller-manager
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/controller-manager.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/controller-manager.yaml
new file mode 100644
index 00000000000..a757441625d
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/controller-manager.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml
new file mode 100644
index 00000000000..17bb2531ff8
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml
@@ -0,0 +1,40 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-leader-election-role
+ namespace: {{ .Release.Namespace }}
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+ - apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml
new file mode 100644
index 00000000000..4966c4ac459
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml
@@ -0,0 +1,16 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-leader-election-rolebinding
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: project-leader-election-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/manager-role.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/manager-role.yaml
new file mode 100644
index 00000000000..921b10f6aa8
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/manager-role.yaml
@@ -0,0 +1,58 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-manager-role
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+ - apiGroups:
+ - ""
+ resources:
+ - pods
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/finalizers
+ verbs:
+ - update
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
+ - patch
+ - update
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml
new file mode 100644
index 00000000000..b82536997bc
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-manager-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-admin-role.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-admin-role.yaml
new file mode 100644
index 00000000000..cfb99887720
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-admin-role.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ 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
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-editor-role.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-editor-role.yaml
new file mode 100644
index 00000000000..0ce7b8da43f
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-editor-role.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-memcached-editor-role
+rules:
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-viewer-role.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-viewer-role.yaml
new file mode 100644
index 00000000000..02a6c7d1e93
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/memcached-viewer-role.yaml
@@ -0,0 +1,24 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-memcached-viewer-role
+rules:
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml
new file mode 100644
index 00000000000..5954a82944e
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml
@@ -0,0 +1,19 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-auth-role
+rules:
+ - apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+ - apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml
new file mode 100644
index 00000000000..33549b4c5ba
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml
@@ -0,0 +1,14 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: project-metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-metrics-auth-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml
new file mode 100644
index 00000000000..609835fe71f
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml
@@ -0,0 +1,11 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-reader
+rules:
+ - nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/getting-started/testdata/project/dist/chart/values.yaml b/docs/book/src/getting-started/testdata/project/dist/chart/values.yaml
new file mode 100644
index 00000000000..0856fc67073
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/chart/values.yaml
@@ -0,0 +1,70 @@
+# Configure the controller manager deployment
+manager:
+ replicas: 1
+
+ image:
+ repository: controller
+ tag: latest
+ pullPolicy: IfNotPresent
+
+ # Arguments
+ args:
+ - --leader-elect
+
+ # Environment variables
+ env: []
+
+ # Pod-level security settings
+ podSecurityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+
+ # Container-level security settings
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+
+ # Resource limits and requests
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+
+# Essential RBAC permissions (required for controller operation)
+# These include ServiceAccount, controller permissions, leader election, and metrics access
+# Note: Essential RBAC is always enabled as it's required for the controller to function
+
+# Helper RBAC roles for managing custom resources
+# These provide convenient admin/editor/viewer roles for each CRD type
+# Useful for giving users different levels of access to your custom resources
+rbacHelpers:
+ enable: false # Install convenience admin/editor/viewer roles for CRDs
+
+# Custom Resource Definitions
+crd:
+ enable: true # Install CRDs with the chart
+ keep: true # Keep CRDs when uninstalling
+
+# Controller metrics endpoint.
+# Enable to expose /metrics endpoint with RBAC protection.
+metrics:
+ enable: true
+ port: 8443 # Metrics server port
+
+# Cert-manager integration for TLS certificates.
+# Required for webhook certificates and metrics endpoint certificates.
+certManager:
+ enable: false
+
+# Prometheus ServiceMonitor for metrics scraping.
+# Requires prometheus-operator to be installed in the cluster.
+prometheus:
+ enable: false
+
diff --git a/docs/book/src/getting-started/testdata/project/dist/install.yaml b/docs/book/src/getting-started/testdata/project/dist/install.yaml
new file mode 100644
index 00000000000..2839de47105
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/dist/install.yaml
@@ -0,0 +1,479 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-system
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: memcacheds.cache.example.com
+spec:
+ group: cache.example.com
+ names:
+ kind: Memcached
+ listKind: MemcachedList
+ plural: memcacheds
+ singular: memcached
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: Memcached is the Schema for the memcacheds API
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: spec defines the desired state of Memcached
+ properties:
+ size:
+ description: |-
+ size defines the number of Memcached instances
+ The following markers will use OpenAPI v3 schema to validate the value
+ More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
+ format: int32
+ maximum: 3
+ minimum: 1
+ type: integer
+ type: object
+ status:
+ description: status defines the observed state of Memcached
+ properties:
+ conditions:
+ description: |-
+ conditions represent the current state of the Memcached resource.
+ Each condition has a unique type and reflects the status of a specific aspect of the resource.
+
+ Standard condition types include:
+ - "Available": the resource is fully functional
+ - "Progressing": the resource is being created or updated
+ - "Degraded": the resource failed to reach or maintain its desired state
+
+ The status of each condition is one of True, False, or Unknown.
+ items:
+ description: Condition contains details for one aspect of the current
+ state of this API Resource.
+ properties:
+ lastTransitionTime:
+ description: |-
+ lastTransitionTime is the last time the condition transitioned from one status to another.
+ This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: |-
+ message is a human readable message indicating details about the transition.
+ This may be an empty string.
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ description: |-
+ observedGeneration represents the .metadata.generation that the condition was set based upon.
+ For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+ with respect to the current state of the instance.
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ description: |-
+ reason contains a programmatic identifier indicating the reason for the condition's last transition.
+ Producers of specific condition types may define expected values and meanings for this field,
+ and whether the values are considered a guaranteed API.
+ The value should be a CamelCase string.
+ This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: status of the condition, one of True, False, Unknown.
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ description: type of condition in CamelCase or in foo.example.com/CamelCase.
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-leader-election-role
+ namespace: project-system
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-manager-role
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+- apiGroups:
+ - ""
+ resources:
+ - pods
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
+ - patch
+ - update
+---
+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
+ app.kubernetes.io/name: project
+ name: project-memcached-editor-role
+rules:
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- 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
+ app.kubernetes.io/name: project
+ name: project-memcached-viewer-role
+rules:
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - cache.example.com
+ resources:
+ - memcacheds/status
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-auth-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-reader
+rules:
+- nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-leader-election-rolebinding
+ namespace: project-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: project-leader-election-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-manager-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: project-metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-metrics-auth-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-service
+ namespace: project-system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: 8443
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager
+ namespace: project-system
+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:
+ - args:
+ - --metrics-bind-address=:8443
+ - --leader-elect
+ - --health-probe-bind-address=:8081
+ command:
+ - /manager
+ image: controller:latest
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ ports: []
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+ volumeMounts: []
+ securityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+ serviceAccountName: project-controller-manager
+ terminationGracePeriodSeconds: 10
+ volumes: []
diff --git a/docs/book/src/getting-started/testdata/project/go.mod b/docs/book/src/getting-started/testdata/project/go.mod
new file mode 100644
index 00000000000..982eaea4a31
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/go.mod
@@ -0,0 +1,100 @@
+module example.com/memcached
+
+go 1.24.6
+
+require (
+ github.com/onsi/ginkgo/v2 v2.22.0
+ github.com/onsi/gomega v1.36.1
+ k8s.io/api v0.34.1
+ k8s.io/apimachinery v0.34.1
+ k8s.io/client-go v0.34.1
+ k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
+ sigs.k8s.io/controller-runtime v0.22.4
+)
+
+require (
+ cel.dev/expr v0.24.0 // indirect
+ github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/blang/semver/v4 v4.0.0 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+ github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fsnotify/fsnotify v1.9.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/go-logr/zapr v1.3.0 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/google/btree v1.1.3 // indirect
+ github.com/google/cel-go v0.26.0 // indirect
+ github.com/google/gnostic-models v0.7.0 // indirect
+ github.com/google/go-cmp v0.7.0 // indirect
+ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_golang v1.22.0 // indirect
+ github.com/prometheus/client_model v0.6.1 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
+ github.com/spf13/cobra v1.9.1 // indirect
+ github.com/spf13/pflag v1.0.6 // indirect
+ github.com/stoewer/go-strcase v1.3.0 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
+ go.opentelemetry.io/otel v1.35.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.35.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.35.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.0 // indirect
+ go.yaml.in/yaml/v2 v2.4.2 // indirect
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
+ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
+ golang.org/x/net v0.38.0 // indirect
+ golang.org/x/oauth2 v0.27.0 // indirect
+ golang.org/x/sync v0.12.0 // indirect
+ golang.org/x/sys v0.31.0 // indirect
+ golang.org/x/term v0.30.0 // indirect
+ golang.org/x/text v0.23.0 // indirect
+ golang.org/x/time v0.9.0 // indirect
+ golang.org/x/tools v0.26.0 // indirect
+ gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
+ google.golang.org/grpc v1.72.1 // indirect
+ google.golang.org/protobuf v1.36.5 // indirect
+ gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/apiextensions-apiserver v0.34.1 // indirect
+ k8s.io/apiserver v0.34.1 // indirect
+ k8s.io/component-base v0.34.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
+ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
+ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+ sigs.k8s.io/randfill v1.0.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
+)
diff --git a/docs/book/src/getting-started/testdata/project/go.sum b/docs/book/src/getting-started/testdata/project/go.sum
new file mode 100644
index 00000000000..3797258ea28
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/go.sum
@@ -0,0 +1,259 @@
+cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
+cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
+github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
+github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
+github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
+go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
+golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
+gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
+google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
+google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
+k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
+k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
+k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
+k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
+k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
+k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
+k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
+k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
+k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
+sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
+sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
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
new file mode 100644
index 00000000000..0a94b7b28b1
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/hack/boilerplate.go.txt
@@ -0,0 +1,15 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
\ No newline at end of file
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
new file mode 100644
index 00000000000..813a23ac93e
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller.go
@@ -0,0 +1,276 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ appsv1 "k8s.io/api/apps/v1"
+ corev1 "k8s.io/api/core/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/utils/ptr"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+
+ cachev1alpha1 "example.com/memcached/api/v1alpha1"
+)
+
+// Definitions to manage status conditions
+const (
+ // typeAvailableMemcached represents the status of the Deployment reconciliation
+ typeAvailableMemcached = "Available"
+)
+
+// MemcachedReconciler reconciles a Memcached object
+type MemcachedReconciler struct {
+ client.Client
+ Scheme *runtime.Scheme
+}
+
+// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/finalizers,verbs=update
+// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
+// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// It is essential for the controller's reconciliation loop to be idempotent. By following the Operator
+// pattern you will create Controllers which provide a reconcile function
+// responsible for synchronizing resources until the desired state is reached on the cluster.
+// Breaking this recommendation goes against the design principles of controller-runtime.
+// and may lead to unforeseen consequences such as resources becoming stuck and requiring manual intervention.
+// For further info:
+// - About Operator Pattern: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
+// - 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.22.4/pkg/reconcile
+func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ log := logf.FromContext(ctx)
+
+ // Fetch the Memcached instance
+ // The purpose is check if the Custom Resource for the Kind Memcached
+ // is applied on the cluster if not we return nil to stop the reconciliation
+ memcached := &cachev1alpha1.Memcached{}
+ err := r.Get(ctx, req.NamespacedName, memcached)
+ if err != nil {
+ if apierrors.IsNotFound(err) {
+ // If the custom resource is not found then it usually means that it was deleted or not created
+ // In this way, we will stop the reconciliation
+ log.Info("memcached resource not found. Ignoring since object must be deleted")
+ return ctrl.Result{}, nil
+ }
+ // Error reading the object - requeue the request.
+ log.Error(err, "Failed to get memcached")
+ return ctrl.Result{}, err
+ }
+
+ // Let's just set the status as Unknown when no status is available
+ if len(memcached.Status.Conditions) == 0 {
+ meta.SetStatusCondition(&memcached.Status.Conditions, metav1.Condition{Type: typeAvailableMemcached, Status: metav1.ConditionUnknown, Reason: "Reconciling", Message: "Starting reconciliation"})
+ if err = r.Status().Update(ctx, memcached); err != nil {
+ log.Error(err, "Failed to update Memcached status")
+ return ctrl.Result{}, err
+ }
+
+ // Let's re-fetch the memcached Custom Resource after updating the status
+ // so that we have the latest state of the resource on the cluster and we will avoid
+ // raising the error "the object has been modified, please apply
+ // your changes to the latest version and try again" which would re-trigger the reconciliation
+ // if we try to update it again in the following operations
+ if err := r.Get(ctx, req.NamespacedName, memcached); err != nil {
+ log.Error(err, "Failed to re-fetch memcached")
+ return ctrl.Result{}, err
+ }
+ }
+
+ // Check if the deployment already exists, if not create a new one
+ found := &appsv1.Deployment{}
+ err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
+ if err != nil && apierrors.IsNotFound(err) {
+ // Define a new deployment
+ dep, err := r.deploymentForMemcached(memcached)
+ if err != nil {
+ log.Error(err, "Failed to define new Deployment resource for Memcached")
+
+ // The following implementation will update the status
+ meta.SetStatusCondition(&memcached.Status.Conditions, metav1.Condition{Type: typeAvailableMemcached,
+ Status: metav1.ConditionFalse, Reason: "Reconciling",
+ Message: fmt.Sprintf("Failed to create Deployment for the custom resource (%s): (%s)", memcached.Name, err)})
+
+ if err := r.Status().Update(ctx, memcached); err != nil {
+ log.Error(err, "Failed to update Memcached status")
+ return ctrl.Result{}, err
+ }
+
+ return ctrl.Result{}, err
+ }
+
+ log.Info("Creating a new Deployment",
+ "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
+ if err = r.Create(ctx, dep); err != nil {
+ log.Error(err, "Failed to create new Deployment",
+ "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
+ return ctrl.Result{}, err
+ }
+
+ // Deployment created successfully
+ // We will requeue the reconciliation so that we can ensure the state
+ // and move forward for the next operations
+ return ctrl.Result{RequeueAfter: time.Minute}, nil
+ } else if err != nil {
+ log.Error(err, "Failed to get Deployment")
+ // Let's return the error for the reconciliation be re-trigged again
+ return ctrl.Result{}, err
+ }
+
+ // If the size is not defined in the Custom Resource then we will set the desired replicas to 0
+ var desiredReplicas int32 = 0
+ if memcached.Spec.Size != nil {
+ desiredReplicas = *memcached.Spec.Size
+ }
+
+ // The CRD API defines that the Memcached type have a MemcachedSpec.Size field
+ // to set the quantity of Deployment instances to the desired state on the cluster.
+ // Therefore, the following code will ensure the Deployment size is the same as defined
+ // via the Size spec of the Custom Resource which we are reconciling.
+ if found.Spec.Replicas == nil || *found.Spec.Replicas != desiredReplicas {
+ found.Spec.Replicas = ptr.To(desiredReplicas)
+ if err = r.Update(ctx, found); err != nil {
+ log.Error(err, "Failed to update Deployment",
+ "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
+
+ // Re-fetch the memcached Custom Resource before updating the status
+ // so that we have the latest state of the resource on the cluster and we will avoid
+ // raising the error "the object has been modified, please apply
+ // your changes to the latest version and try again" which would re-trigger the reconciliation
+ if err := r.Get(ctx, req.NamespacedName, memcached); err != nil {
+ log.Error(err, "Failed to re-fetch memcached")
+ return ctrl.Result{}, err
+ }
+
+ // The following implementation will update the status
+ meta.SetStatusCondition(&memcached.Status.Conditions, metav1.Condition{Type: typeAvailableMemcached,
+ Status: metav1.ConditionFalse, Reason: "Resizing",
+ Message: fmt.Sprintf("Failed to update the size for the custom resource (%s): (%s)", memcached.Name, err)})
+
+ if err := r.Status().Update(ctx, memcached); err != nil {
+ log.Error(err, "Failed to update Memcached status")
+ return ctrl.Result{}, err
+ }
+
+ return ctrl.Result{}, err
+ }
+
+ // Now, that we update the size we want to requeue the reconciliation
+ // so that we can ensure that we have the latest state of the resource before
+ // update. Also, it will help ensure the desired state on the cluster
+ return ctrl.Result{Requeue: true}, nil
+ }
+
+ // The following implementation will update the status
+ meta.SetStatusCondition(&memcached.Status.Conditions, metav1.Condition{Type: typeAvailableMemcached,
+ Status: metav1.ConditionTrue, Reason: "Reconciling",
+ Message: fmt.Sprintf("Deployment for custom resource (%s) with %d replicas created successfully", memcached.Name, desiredReplicas)})
+
+ if err := r.Status().Update(ctx, memcached); err != nil {
+ log.Error(err, "Failed to update Memcached status")
+ return ctrl.Result{}, err
+ }
+
+ return ctrl.Result{}, nil
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&cachev1alpha1.Memcached{}).
+ Owns(&appsv1.Deployment{}).
+ Named("memcached").
+ Complete(r)
+}
+
+// deploymentForMemcached returns a Memcached Deployment object
+func (r *MemcachedReconciler) deploymentForMemcached(
+ memcached *cachev1alpha1.Memcached) (*appsv1.Deployment, error) {
+ image := "memcached:1.6.26-alpine3.19"
+
+ dep := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: memcached.Name,
+ Namespace: memcached.Namespace,
+ },
+ Spec: appsv1.DeploymentSpec{
+ Replicas: memcached.Spec.Size,
+ Selector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"app.kubernetes.io/name": "project"},
+ },
+ Template: corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: map[string]string{"app.kubernetes.io/name": "project"},
+ },
+ Spec: corev1.PodSpec{
+ SecurityContext: &corev1.PodSecurityContext{
+ RunAsNonRoot: ptr.To(true),
+ SeccompProfile: &corev1.SeccompProfile{
+ Type: corev1.SeccompProfileTypeRuntimeDefault,
+ },
+ },
+ Containers: []corev1.Container{{
+ Image: image,
+ Name: "memcached",
+ ImagePullPolicy: corev1.PullIfNotPresent,
+ // Ensure restrictive context for the container
+ // More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
+ SecurityContext: &corev1.SecurityContext{
+ RunAsNonRoot: ptr.To(true),
+ RunAsUser: ptr.To(int64(1001)),
+ AllowPrivilegeEscalation: ptr.To(false),
+ Capabilities: &corev1.Capabilities{
+ Drop: []corev1.Capability{
+ "ALL",
+ },
+ },
+ },
+ Ports: []corev1.ContainerPort{{
+ ContainerPort: 11211,
+ Name: "memcached",
+ }},
+ Command: []string{"memcached", "--memory-limit=64", "-o", "modern", "-v"},
+ }},
+ },
+ },
+ },
+ }
+
+ // Set the ownerRef for the Deployment
+ // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/
+ if err := ctrl.SetControllerReference(memcached, dep, r.Scheme); err != nil {
+ return nil, err
+ }
+ return dep, nil
+}
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
new file mode 100644
index 00000000000..3e7ec14ce63
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/internal/controller/memcached_controller_test.go
@@ -0,0 +1,107 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controller
+
+import (
+ "context"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ appsv1 "k8s.io/api/apps/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/types"
+ "k8s.io/utils/ptr"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ cachev1alpha1 "example.com/memcached/api/v1alpha1"
+)
+
+var _ = Describe("Memcached Controller", func() {
+ Context("When reconciling a resource", func() {
+ const resourceName = "test-resource"
+
+ ctx := context.Background()
+
+ typeNamespacedName := types.NamespacedName{
+ Name: resourceName,
+ Namespace: "default", // TODO(user):Modify as needed
+ }
+ memcached := &cachev1alpha1.Memcached{}
+
+ BeforeEach(func() {
+ By("creating the custom resource for the Kind Memcached")
+ err := k8sClient.Get(ctx, typeNamespacedName, memcached)
+ if err != nil && errors.IsNotFound(err) {
+ resource := &cachev1alpha1.Memcached{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: resourceName,
+ Namespace: "default",
+ },
+ Spec: cachev1alpha1.MemcachedSpec{
+ Size: ptr.To(int32(1)),
+ },
+ }
+ Expect(k8sClient.Create(ctx, resource)).To(Succeed())
+ }
+ })
+
+ AfterEach(func() {
+ // TODO(user): Cleanup logic after each test, like removing the resource instance.
+ resource := &cachev1alpha1.Memcached{}
+ err := k8sClient.Get(ctx, typeNamespacedName, resource)
+ Expect(err).NotTo(HaveOccurred())
+
+ By("Cleanup the specific resource instance Memcached")
+ Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
+ })
+ It("should successfully reconcile the resource", func() {
+ By("Reconciling the created resource")
+ controllerReconciler := &MemcachedReconciler{
+ Client: k8sClient,
+ Scheme: k8sClient.Scheme(),
+ }
+
+ _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
+ NamespacedName: typeNamespacedName,
+ })
+ Expect(err).NotTo(HaveOccurred())
+ By("Checking if Deployment was successfully created in the reconciliation")
+ Eventually(func(g Gomega) {
+ found := &appsv1.Deployment{}
+ g.Expect(k8sClient.Get(ctx, typeNamespacedName, found)).To(Succeed())
+ }).Should(Succeed())
+
+ By("Reconciling the custom resource again")
+ _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{
+ NamespacedName: typeNamespacedName,
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ By("Checking the latest Status Condition added to the Memcached instance")
+ Expect(k8sClient.Get(ctx, typeNamespacedName, memcached)).To(Succeed())
+ var conditions []metav1.Condition
+ Expect(memcached.Status.Conditions).To(ContainElement(
+ HaveField("Type", Equal(typeAvailableMemcached)), &conditions))
+ Expect(conditions).To(HaveLen(1), "Multiple conditions of type %s", typeAvailableMemcached)
+ Expect(conditions[0].Status).To(Equal(metav1.ConditionTrue), "condition %s", typeAvailableMemcached)
+ Expect(conditions[0].Reason).To(Equal("Reconciling"), "condition %s", typeAvailableMemcached)
+ })
+ })
+})
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
new file mode 100644
index 00000000000..d97f2ecae76
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/internal/controller/suite_test.go
@@ -0,0 +1,116 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controller
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+ cachev1alpha1 "example.com/memcached/api/v1alpha1"
+ // +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 (
+ ctx context.Context
+ cancel context.CancelFunc
+ testEnv *envtest.Environment
+ cfg *rest.Config
+ k8sClient client.Client
+)
+
+func TestControllers(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Controller Suite")
+}
+
+var _ = BeforeSuite(func() {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+
+ 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,
+ }
+
+ // 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())
+
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+})
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ cancel()
+ 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
new file mode 100644
index 00000000000..e7589452e0f
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_suite_test.go
@@ -0,0 +1,92 @@
+//go:build e2e
+// +build e2e
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "example.com/memcached/test/utils"
+)
+
+var (
+ // Optional Environment Variables:
+ // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup.
+ // These variables are useful if CertManager is already installed, avoiding
+ // re-installation and conflicts.
+ skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true"
+ // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster
+ isCertManagerAlreadyInstalled = false
+
+ // projectImage is the name of the image which will be build and loaded
+ // with the code source changes to be tested.
+ projectImage = "example.com/project:v0.0.1"
+)
+
+// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated,
+// temporary environment to validate project changes with the purpose of being used in CI jobs.
+// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs
+// CertManager.
+func TestE2E(t *testing.T) {
+ RegisterFailHandler(Fail)
+ _, _ = fmt.Fprintf(GinkgoWriter, "Starting project integration test suite\n")
+ RunSpecs(t, "e2e suite")
+}
+
+var _ = BeforeSuite(func() {
+ By("building the manager(Operator) image")
+ cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
+ _, err := utils.Run(cmd)
+ ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")
+
+ // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
+ // built and available before running the tests. Also, remove the following block.
+ By("loading the manager(Operator) image on Kind")
+ err = utils.LoadImageToKindClusterWithName(projectImage)
+ ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind")
+
+ // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing.
+ // To prevent errors when tests run in environments with CertManager already installed,
+ // we check for its presence before execution.
+ // Setup CertManager before the suite if not skipped and if not already installed
+ if !skipCertManagerInstall {
+ By("checking if cert manager is installed already")
+ isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled()
+ if !isCertManagerAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n")
+ Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager")
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n")
+ }
+ }
+})
+
+var _ = AfterSuite(func() {
+ // Teardown CertManager after the suite if not skipped and if it was not already installed
+ if !skipCertManagerInstall && !isCertManagerAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n")
+ utils.UninstallCertManager()
+ }
+})
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
new file mode 100644
index 00000000000..d4b206e9618
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/test/e2e/e2e_test.go
@@ -0,0 +1,337 @@
+//go:build e2e
+// +build e2e
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "example.com/memcached/test/utils"
+)
+
+// namespace where the project is deployed in
+const namespace = "project-system"
+
+// serviceAccountName created for the project
+const serviceAccountName = "project-controller-manager"
+
+// metricsServiceName is the name of the metrics service of the project
+const metricsServiceName = "project-controller-manager-metrics-service"
+
+// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data
+const metricsRoleBindingName = "project-metrics-binding"
+
+var _ = Describe("Manager", Ordered, func() {
+ var controllerPodName string
+
+ // Before running the tests, set up the environment by creating the namespace,
+ // 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)
+ Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs")
+
+ By("deploying the controller-manager")
+ cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage))
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager")
+ })
+
+ // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs,
+ // and deleting the namespace.
+ AfterAll(func() {
+ By("cleaning up the curl pod for metrics")
+ cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace)
+ _, _ = utils.Run(cmd)
+
+ By("undeploying the controller-manager")
+ cmd = exec.Command("make", "undeploy")
+ _, _ = utils.Run(cmd)
+
+ By("uninstalling CRDs")
+ cmd = exec.Command("make", "uninstall")
+ _, _ = utils.Run(cmd)
+
+ By("removing manager namespace")
+ cmd = exec.Command("kubectl", "delete", "ns", namespace)
+ _, _ = utils.Run(cmd)
+ })
+
+ // After each test, check for failures and collect logs, events,
+ // and pod descriptions for debugging.
+ AfterEach(func() {
+ specReport := CurrentSpecReport()
+ if specReport.Failed() {
+ By("Fetching controller manager pod logs")
+ cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
+ controllerLogs, err := utils.Run(cmd)
+ if err == nil {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs)
+ } else {
+ _, _ = 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, "Kubernetes events:\n%s", eventsOutput)
+ } else {
+ _, _ = 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, "Metrics logs:\n %s", metricsOutput)
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err)
+ }
+
+ By("Fetching controller manager pod description")
+ cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace)
+ podDescription, err := utils.Run(cmd)
+ if err == nil {
+ fmt.Println("Pod description:\n", podDescription)
+ } else {
+ fmt.Println("Failed to describe controller pod")
+ }
+ }
+ })
+
+ SetDefaultEventuallyTimeout(2 * time.Minute)
+ SetDefaultEventuallyPollingInterval(time.Second)
+
+ Context("Manager", func() {
+ It("should run successfully", func() {
+ By("validating that the controller-manager pod is running as expected")
+ verifyControllerUp := func(g Gomega) {
+ // Get the name of the controller-manager pod
+ cmd := exec.Command("kubectl", "get",
+ "pods", "-l", "control-plane=controller-manager",
+ "-o", "go-template={{ range .items }}"+
+ "{{ if not .metadata.deletionTimestamp }}"+
+ "{{ .metadata.name }}"+
+ "{{ \"\\n\" }}{{ end }}{{ end }}",
+ "-n", namespace,
+ )
+
+ podOutput, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information")
+ podNames := utils.GetNonEmptyLines(podOutput)
+ g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running")
+ controllerPodName = podNames[0]
+ g.Expect(controllerPodName).To(ContainSubstring("controller-manager"))
+
+ // Validate the pod's status
+ cmd = exec.Command("kubectl", "get",
+ "pods", controllerPodName, "-o", "jsonpath={.status.phase}",
+ "-n", namespace,
+ )
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status")
+ }
+ Eventually(verifyControllerUp).Should(Succeed())
+ })
+
+ It("should ensure the metrics endpoint is serving metrics", func() {
+ By("creating a ClusterRoleBinding for the service account to allow access to metrics")
+ cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName,
+ "--clusterrole=project-metrics-reader",
+ fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName),
+ )
+ _, err := utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding")
+
+ By("validating that the metrics service is available")
+ cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace)
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Metrics service should exist")
+
+ By("getting the service account token")
+ token, err := serviceAccountToken()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(token).NotTo(BeEmpty())
+
+ By("ensuring the controller pod is ready")
+ verifyControllerPodReady := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "pod", controllerPodName, "-n", namespace,
+ "-o", "jsonpath={.status.conditions[?(@.type=='Ready')].status}")
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("True"), "Controller pod not ready")
+ }
+ Eventually(verifyControllerPodReady, 3*time.Minute, time.Second).Should(Succeed())
+
+ By("verifying that the controller manager is serving the metrics server")
+ verifyMetricsServerStarted := func(g Gomega) {
+ cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(ContainSubstring("Serving metrics server"),
+ "Metrics server not yet started")
+ }
+ Eventually(verifyMetricsServerStarted, 3*time.Minute, time.Second).Should(Succeed())
+
+ // +kubebuilder:scaffold:e2e-metrics-webhooks-readiness
+
+ 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: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": {
+ "readOnlyRootFilesystem": true,
+ "allowPrivilegeEscalation": false,
+ "capabilities": {
+ "drop": ["ALL"]
+ },
+ "runAsNonRoot": true,
+ "runAsUser": 1000,
+ "seccompProfile": {
+ "type": "RuntimeDefault"
+ }
+ }
+ }],
+ "serviceAccountName": "%s"
+ }
+ }`, token, metricsServiceName, namespace, serviceAccountName))
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod")
+
+ By("waiting for the curl-metrics pod to complete.")
+ verifyCurlUp := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "pods", "curl-metrics",
+ "-o", "jsonpath={.status.phase}",
+ "-n", namespace)
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status")
+ }
+ Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed())
+
+ By("getting the metrics by checking curl-metrics logs")
+ verifyMetricsAvailable := func(g Gomega) {
+ metricsOutput, err := getMetricsOutput()
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
+ g.Expect(metricsOutput).NotTo(BeEmpty())
+ g.Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK"))
+ }
+ Eventually(verifyMetricsAvailable, 2*time.Minute).Should(Succeed())
+ })
+
+ // +kubebuilder:scaffold:e2e-webhooks-checks
+
+ // TODO: Customize the e2e test suite with scenarios specific to your project.
+ // Consider applying sample/CR(s) and check their status and/or verifying
+ // the reconciliation by using the metrics, i.e.:
+ // metricsOutput, err := getMetricsOutput()
+ // Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
+ // Expect(metricsOutput).To(ContainSubstring(
+ // fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`,
+ // strings.ToLower(),
+ // ))
+ })
+})
+
+// serviceAccountToken returns a token for the specified service account in the given namespace.
+// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request
+// and parsing the resulting token from the API response.
+func serviceAccountToken() (string, error) {
+ const tokenRequestRawString = `{
+ "apiVersion": "authentication.k8s.io/v1",
+ "kind": "TokenRequest"
+ }`
+
+ // Temporary file to store the token request
+ secretName := fmt.Sprintf("%s-token-request", serviceAccountName)
+ tokenRequestFile := filepath.Join("/tmp", secretName)
+ err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644))
+ if err != nil {
+ return "", err
+ }
+
+ var out string
+ verifyTokenCreation := func(g Gomega) {
+ // Execute kubectl command to create the token
+ cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf(
+ "/api/v1/namespaces/%s/serviceaccounts/%s/token",
+ namespace,
+ serviceAccountName,
+ ), "-f", tokenRequestFile)
+
+ output, err := cmd.CombinedOutput()
+ g.Expect(err).NotTo(HaveOccurred())
+
+ // Parse the JSON output to extract the token
+ var token tokenRequest
+ err = json.Unmarshal(output, &token)
+ g.Expect(err).NotTo(HaveOccurred())
+
+ out = token.Status.Token
+ }
+ Eventually(verifyTokenCreation).Should(Succeed())
+
+ return out, err
+}
+
+// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint.
+func getMetricsOutput() (string, error) {
+ By("getting the curl-metrics logs")
+ cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace)
+ return utils.Run(cmd)
+}
+
+// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response,
+// containing only the token field that we need to extract.
+type tokenRequest struct {
+ Status struct {
+ Token string `json:"token"`
+ } `json:"status"`
+}
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
new file mode 100644
index 00000000000..5872cb92f89
--- /dev/null
+++ b/docs/book/src/getting-started/testdata/project/test/utils/utils.go
@@ -0,0 +1,226 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package utils
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck
+)
+
+const (
+ certmanagerVersion = "v1.19.1"
+ certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
+
+ defaultKindBinary = "kind"
+ defaultKindCluster = "kind"
+)
+
+func warnError(err error) {
+ _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
+}
+
+// Run executes the provided command within this context
+func Run(cmd *exec.Cmd) (string, error) {
+ dir, _ := GetProjectDir()
+ cmd.Dir = dir
+
+ if err := os.Chdir(cmd.Dir); err != nil {
+ _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err)
+ }
+
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+ command := strings.Join(cmd.Args, " ")
+ _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err)
+ }
+
+ return string(output), nil
+}
+
+// UninstallCertManager uninstalls the cert manager
+func UninstallCertManager() {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+
+ // Delete leftover leases in kube-system (not cleaned by default)
+ kubeSystemLeases := []string{
+ "cert-manager-cainjector-leader-election",
+ "cert-manager-controller",
+ }
+ for _, lease := range kubeSystemLeases {
+ cmd = exec.Command("kubectl", "delete", "lease", lease,
+ "-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+ }
+}
+
+// InstallCertManager installs the cert manager bundle.
+func InstallCertManager() error {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "apply", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ return err
+ }
+ // Wait for cert-manager-webhook to be ready, which can take time if cert-manager
+ // was re-installed after uninstalling on a cluster.
+ cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
+ "--for", "condition=Available",
+ "--namespace", "cert-manager",
+ "--timeout", "5m",
+ )
+
+ _, err := Run(cmd)
+ return err
+}
+
+// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed
+// by verifying the existence of key CRDs related to Cert Manager.
+func IsCertManagerCRDsInstalled() bool {
+ // List of common Cert Manager CRDs
+ certManagerCRDs := []string{
+ "certificates.cert-manager.io",
+ "issuers.cert-manager.io",
+ "clusterissuers.cert-manager.io",
+ "certificaterequests.cert-manager.io",
+ "orders.acme.cert-manager.io",
+ "challenges.acme.cert-manager.io",
+ }
+
+ // Execute the kubectl command to get all CRDs
+ cmd := exec.Command("kubectl", "get", "crds")
+ output, err := Run(cmd)
+ if err != nil {
+ return false
+ }
+
+ // Check if any of the Cert Manager CRDs are present
+ crdList := GetNonEmptyLines(output)
+ for _, crd := range certManagerCRDs {
+ for _, line := range crdList {
+ if strings.Contains(line, crd) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// LoadImageToKindClusterWithName loads a local docker image to the kind cluster
+func LoadImageToKindClusterWithName(name string) error {
+ cluster := defaultKindCluster
+ if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
+ cluster = v
+ }
+ kindOptions := []string{"load", "docker-image", name, "--name", cluster}
+ kindBinary := defaultKindBinary
+ if v, ok := os.LookupEnv("KIND"); ok {
+ kindBinary = v
+ }
+ cmd := exec.Command(kindBinary, kindOptions...)
+ _, err := Run(cmd)
+ return err
+}
+
+// GetNonEmptyLines converts given command output string into individual objects
+// according to line breakers, and ignores the empty elements in it.
+func GetNonEmptyLines(output string) []string {
+ var res []string
+ elements := strings.Split(output, "\n")
+ for _, element := range elements {
+ if element != "" {
+ res = append(res, element)
+ }
+ }
+
+ return res
+}
+
+// GetProjectDir will return the directory where the project is
+func GetProjectDir() (string, error) {
+ wd, err := os.Getwd()
+ if err != nil {
+ return wd, fmt.Errorf("failed to get current working directory: %w", err)
+ }
+ wd = strings.ReplaceAll(wd, "/test/e2e", "")
+ return wd, nil
+}
+
+// UncommentCode searches for target in the file and remove the comment prefix
+// of the target content. The target content may span multiple lines.
+func UncommentCode(filename, target, prefix string) error {
+ // false positive
+ // nolint:gosec
+ content, err := os.ReadFile(filename)
+ if err != nil {
+ return fmt.Errorf("failed to read file %q: %w", filename, err)
+ }
+ strContent := string(content)
+
+ idx := strings.Index(strContent, target)
+ if idx < 0 {
+ return fmt.Errorf("unable to find the code %q to be uncomment", target)
+ }
+
+ out := new(bytes.Buffer)
+ _, err = out.Write(content[:idx])
+ if err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+
+ scanner := bufio.NewScanner(bytes.NewBufferString(target))
+ if !scanner.Scan() {
+ return nil
+ }
+ for {
+ if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+ // Avoid writing a newline in case the previous line was the last in target.
+ if !scanner.Scan() {
+ break
+ }
+ if _, err = out.WriteString("\n"); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+ }
+
+ if _, err = out.Write(content[idx+len(target):]); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+
+ // false positive
+ // nolint:gosec
+ if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil {
+ return fmt.Errorf("failed to write file %q: %w", filename, err)
+ }
+
+ return nil
+}
diff --git a/docs/book/src/introduction.md b/docs/book/src/introduction.md
index ded98d14ee0..81945cb02a5 100644
--- a/docs/book/src/introduction.md
+++ b/docs/book/src/introduction.md
@@ -1,8 +1,7 @@
-**Note:** Impatient readers may head straight to [Quick
-Start](quick-start.md).
+**Note:** Impatient readers may head straight to [Quick Start](quick-start.md).
-**Using Kubebuilder v1? Check the [legacy
-documentation](https://book-v1.book.kubebuilder.io)**
+**Using previous version of Kubebuilder v1 or v2?**
+**Check the legacy documentation for [v1](https://book-v1.book.kubebuilder.io), [v2](https://book-v2.book.kubebuilder.io) or [v3](https://book-v3.book.kubebuilder.io)**
## Who is this for
@@ -25,7 +24,7 @@ Including:
#### Kubernetes API extension developers
-API extension developers will learn the principals and concepts behind implementing canonical
+API extension developers will learn the principles and concepts behind implementing canonical
Kubernetes APIs, as well as simple tools and libraries for rapid execution. This
book covers pitfalls and misconceptions that extension developers commonly encounter.
@@ -53,8 +52,8 @@ common tooling to manage the objects.
Building services as Kubernetes APIs provides many advantages to plain old REST, including:
* Hosted API endpoints, storage, and validation.
-* Rich tooling and clis such as `kubectl` and `kustomize`.
-* Support for Authn and granular Authz.
+* Rich tooling and CLIs such as `kubectl` and `kustomize`.
+* Support for AuthN and granular AuthZ.
* Support for API evolution through API versioning and conversion.
* Facilitation of adaptive / self-healing APIs that continuously respond to changes
in the system state without user intervention.
diff --git a/docs/book/src/logos/README.md b/docs/book/src/logos/README.md
index 45301b42527..2664af2bf78 100644
--- a/docs/book/src/logos/README.md
+++ b/docs/book/src/logos/README.md
@@ -1,12 +1,12 @@
-# KubeBuilder Logos
+# Kubebuilder Logos
The official location for the logos is in a [public GCS
bucket][kb-logos-gcs] (or if you like GCS XML listings,
[here][kb-logos-gcs-direct]).
These logos are copies used in the book, resized to their appropriate
-sizes.
+sizes.
-[kb-logos-gcs]: https://console.cloud.google.com/storage/browser/kubebuilder-logos
+[kb-logos-gcs]: https://console.cloud.google.com/storage/browser/kubebuilder-logos
[kb-logos-gcs-direct]: https://storage.googleapis.com/kubebuilder-logos
diff --git a/docs/book/src/logos/favicon.png b/docs/book/src/logos/favicon.png
deleted file mode 100644
index f3587603e3c..00000000000
Binary files a/docs/book/src/logos/favicon.png and /dev/null differ
diff --git a/docs/book/src/logos/kb-icon.svg b/docs/book/src/logos/kb-icon.svg
new file mode 100644
index 00000000000..8d1d659c5b4
--- /dev/null
+++ b/docs/book/src/logos/kb-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/book/src/logos/kb-logo-one-line.svg b/docs/book/src/logos/kb-logo-one-line.svg
new file mode 100644
index 00000000000..25cc8d0f898
--- /dev/null
+++ b/docs/book/src/logos/kb-logo-one-line.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/book/src/logos/logo-single-line.png b/docs/book/src/logos/logo-single-line.png
deleted file mode 100644
index 23968e7c617..00000000000
Binary files a/docs/book/src/logos/logo-single-line.png and /dev/null differ
diff --git a/docs/book/src/migration/legacy.md b/docs/book/src/migration/legacy.md
new file mode 100644
index 00000000000..da2ff198a66
--- /dev/null
+++ b/docs/book/src/migration/legacy.md
@@ -0,0 +1,18 @@
+# Migration guides from Legacy versions < 3.0.0
+
+Follow the migration guides from the legacy Kubebuilder versions up the required latest v3x version.
+Note that from v3, a new ecosystem using plugins is introduced for better maintainability, reusability and user
+experience .
+
+For more info, see the design docs of:
+
+- [Extensible CLI and Scaffolding Plugins: phase 1][plugins-phase1-design-doc]
+- [Extensible CLI and Scaffolding Plugins: phase 1.5][plugins-phase1-design-doc-1.5]
+- [Extensible CLI and Scaffolding Plugins - Phase 2][plugins-phase2-design-doc]
+
+Also, you can check the [Plugins section][plugins-section].
+
+[plugins-phase1-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1.md
+[plugins-phase1-design-doc-1.5]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1-5.md
+[plugins-phase2-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-2.md
+[plugins-section]: ./../plugins/plugins.md
diff --git a/docs/book/src/migration/manually_migration_guide_v2_v3.md b/docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md
similarity index 82%
rename from docs/book/src/migration/manually_migration_guide_v2_v3.md
rename to docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md
index 3a995e76bcd..f8fc825e725 100644
--- a/docs/book/src/migration/manually_migration_guide_v2_v3.md
+++ b/docs/book/src/migration/legacy/manually_migration_guide_v2_v3.md
@@ -8,20 +8,20 @@ to install the required components.
The following guide describes the manual steps required to upgrade your config version and start using the plugin-enabled version.
-This way is more complex, susceptible to errors, and success cannot be assured. Also, by following these steps you will not get the improvements and bug fixes in the default generated project files.
+This way is more complex, susceptible to errors, and success cannot be assured. Also, by following these steps you will not get the improvements and bug fixes in the default generated project files.
Usually you will only try to do it manually if you customized your project and deviated too much from the proposed scaffold. Before continuing, ensure that you understand the note about [project customizations][project-customizations]. Note that you might need to spend more effort to do this process manually than organize your project customizations to follow up the proposed layout and keep your project maintainable and upgradable with less effort in the future.
The recommended upgrade approach is to follow the [Migration Guide v2 to V3][migration-guide-v2-to-v3] instead.
-## Migration from project config version "2" to "3"
+## Migration from project config version "2" to "3"
Migrating between project configuration versions involves additions, removals, and/or changes
to fields in your project's `PROJECT` file, which is created by running the `init` command.
The `PROJECT` file now has a new layout. It stores more information about what resources are in use, to better enable plugins to make useful decisions when scaffolding.
-
-Furthermore, the `PROJECT` file itself is now versioned. The `version` field corresponds to the version of the `PROJECT` file itself, while the `layout` field indicates the scaffolding and the primary plugin version in use.
+
+Furthermore, the `PROJECT` file itself is now versioned. The `version` field corresponds to the version of the `PROJECT` file itself, while the `layout` field indicates the scaffolding and the primary plugin version in use.
### Steps to migrate
@@ -43,7 +43,7 @@ The default plugin layout which is equivalent to the previous version is `go.kub
```yaml
...
-layout:
+layout:
- go.kubebuilder.io/v2
...
```
@@ -59,12 +59,12 @@ version: "3"
```
#### Add the resource data
-
+
The attribute `resources` represents the list of resources scaffolded in your project.
You will need to add the following data for each resource added to the project.
-##### Add the Kubernetes API version by adding `resources[entry].api.crdVersion: v1beta1`:
+##### Add the Kubernetes API version by adding `resources[entry].api.crdVersion: v1beta1`:
```yaml
...
@@ -77,7 +77,7 @@ resources:
kind: Guestbook
...
```
-
+
##### Add the scope used do scaffold the CRDs by adding `resources[entry].api.namespaced: true` unless they were cluster-scoped:
```yaml
@@ -98,7 +98,7 @@ resources:
resources:
- api:
...
- controller: true
+ controller: true
group: webapp
kind: Guestbook
```
@@ -118,19 +118,19 @@ resources:
Supportability
-Kubebuilder only supports core types and the APIs scaffolded in the project by default unless you manually change the files you will be unable to work with external-types.
+Kubebuilder only supports core types and the APIs scaffolded in the project by default unless you manually change the files you will be unable to work with external-types.
For core types, the domain value will be `k8s.io` or empty.
-
+
However, for an external-type you might leave this attribute empty. We cannot suggest what would be the best approach in this case until it become officially supported by the tool. For further information check the issue [#1999][issue-1999].
Note that you will only need to add the `domain` if your project has a scaffold for a core type API which the `Domain` value is not empty in Kubernetes API group qualified scheme definition. (For example, see [here](https://github.com/kubernetes/api/blob/v0.19.7/apps/v1/register.go#L26) that for Kinds from the API `apps` it has not a domain when see [here](https://github.com/kubernetes/api/blob/v0.19.7/authentication/v1/register.go#L26) that for Kinds from the API `authentication` its domain is `k8s.io` )
-
+
Check the following the list to know the core types supported and its domain:
-| Core Type | Domain |
+| Core Type | Domain |
|----------|:-------------:|
| admission | "k8s.io" |
| admissionregistration | "k8s.io" |
@@ -173,7 +173,7 @@ Following an example where a controller was scaffold for the core type Kind Depl
If you did not scaffold an API but only generate a controller for the API(GKV) informed then, you do not need to add the path. Note, that it usually happens when you add a controller for an external or core type.
-Kubebuilder only supports core types and the APIs scaffolded in the project by default unless you manually change the files you will be unable to work with external-types.
+Kubebuilder only supports core types and the APIs scaffolded in the project by default unless you manually change the files you will be unable to work with external-types.
The path will always be the import path used in your Go files to use the API.
@@ -188,7 +188,7 @@ resources:
group: webapp
kind: Guestbook
path: example/api/v1
-```
+```
##### If your project is using webhooks then, add `resources[entry].webhooks.[type]: true` for each type generated and then, add `resources[entry].webhooks.webhookVersion: v1beta1`:
@@ -197,7 +197,7 @@ resources:
The valid types are: `defaulting`, `validation` and `conversion`. Use the webhook type used to scaffold the project.
-The Kubernetes API version used to do the webhooks scaffolds in `Kubebuilder v2` is `v1beta1`. Then, you will add the `webhookVersion: v1beta1` for all cases.
+The Kubernetes API version used to do the webhooks scaffolds in `Kubebuilder v2` is `v1beta1`. Then, you will add the `webhookVersion: v1beta1` for all cases.
@@ -215,14 +215,14 @@ resources:
```
#### Check your PROJECT file
-
+
Now ensure that your `PROJECT` file has the same information when the manifests are generated via Kubebuilder V3 CLI.
For the QuickStart example, the `PROJECT` file manually updated to use `go.kubebuilder.io/v2` would look like:
```yaml
domain: my.domain
-layout:
+layout:
- go.kubebuilder.io/v2
projectName: example
repo: example
@@ -263,7 +263,7 @@ version: "2"
```yaml
domain: testproject.org
-layout:
+layout:
- go.kubebuilder.io/v2
projectName: example
repo: sigs.k8s.io/kubebuilder/example
@@ -312,7 +312,7 @@ version: "3"
In the steps above, you updated only the `PROJECT` file which represents the project configuration. This configuration is useful only for the CLI tool. It should not affect how your project behaves.
-There is no option to verify that you properly updated the configuration file. The best way to ensure the configuration file has the correct `V3+` fields is to initialize a project with the same API(s), controller(s), and webhook(s) in order to compare generated configuration with the manually changed configuration.
+There is no option to verify that you properly updated the configuration file. The best way to ensure the configuration file has the correct `V3+` fields is to initialize a project with the same API(s), controller(s), and webhook(s) in order to compare generated configuration with the manually changed configuration.
If you made mistakes in the above process, you will likely face issues using the CLI.
@@ -324,12 +324,12 @@ to files created by any plugin-supported command, e.g. `init` and `create`. A pl
one or more project config versions; make sure you upgrade your project's
config version to the latest supported by your target plugin version before upgrading plugin versions.
-The following steps describe the manual changes required to modify the project's layout enabling your project to use the `go/v3` plugin. These steps will not help you address all the bug fixes of the already generated scaffolds.
+The following steps describe the manual changes required to modify the project's layout enabling your project to use the `go/v3` plugin. These steps will not help you address all the bug fixes of the already generated scaffolds.
-
- Deprecated APIs
+
+ Deprecated APIs
-The following steps will not migrate the API versions which are deprecated `apiextensions.k8s.io/v1beta1`, `admissionregistration.k8s.io/v1beta1`, `cert-manager.io/v1alpha2`.
+The following steps will not migrate the API versions which are deprecated `apiextensions.k8s.io/v1beta1`, `admissionregistration.k8s.io/v1beta1`, `cert-manager.io/v1alpha2`.
@@ -341,43 +341,44 @@ Before updating the `layout`, please ensure you have followed the above steps to
```yaml
domain: my.domain
-layout:
+layout:
- go.kubebuilder.io/v3
...
```
-#### Upgrade the Go version and its dependencies:
+#### Upgrade the Go version and its dependencies:
Ensure that your `go.mod` is using Go version `1.15` and the following dependency versions:
```go
module example
-go 1.15
+go 1.18
require (
- github.com/go-logr/logr v0.3.0
- github.com/onsi/ginkgo v1.14.1
- github.com/onsi/gomega v1.10.2
- k8s.io/apimachinery v0.19.2
- k8s.io/client-go v0.19.2
- sigs.k8s.io/controller-runtime v0.7.0
+ github.com/onsi/ginkgo/v2 v2.1.4
+ github.com/onsi/gomega v1.19.0
+ k8s.io/api v0.24.0
+ k8s.io/apimachinery v0.24.0
+ k8s.io/client-go v0.24.0
+ sigs.k8s.io/controller-runtime v0.12.1
)
+
```
-#### Update the golang image
+#### Update the golang image
In the Dockerfile, replace:
```
# Build the manager binary
-FROM golang:1.13 as builder
+FROM docker.io/golang:1.13 as builder
```
With:
```
# Build the manager binary
-FROM golang:1.15 as builder
+FROM docker.io/golang:1.16 as builder
```
#### Update your Makefile
@@ -393,7 +394,7 @@ CRD_OPTIONS ?= "crd:trivialVersions=true"
With:
```
-CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
+CRD_OPTIONS ?= "crd"
```
##### To allow automatic downloads
@@ -410,7 +411,6 @@ With:
```makefile
# Setting SHELL to bash allows bash commands to be executed by recipes.
-# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
@@ -425,14 +425,14 @@ test: manifests generate fmt vet ## Run tests.
Envtest binaries
-The Kubernetes binaries that are required for the Envtest were upgraded from `1.16.4` to `1.19.2`.
+The Kubernetes binaries that are required for the Envtest were upgraded from `1.16.4` to `1.22.1`.
You can still install them globally by following [these installation instructions][doc-envtest].
##### To upgrade `controller-gen` and `kustomize` dependencies versions used
-To upgrade the `controller-gen` and `kustomize` version used to generate the manifests replace:
+To upgrade the `controller-gen` and `kustomize` version used to generate the manifests replace:
```
# find or download controller-gen
@@ -456,27 +456,37 @@ endif
With:
```
-CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
-controller-gen: ## Download controller-gen locally if necessary.
- $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
-
-KUSTOMIZE = $(shell pwd)/bin/kustomize
-kustomize: ## Download kustomize locally if necessary.
- $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
-
-# go-get-tool will 'go get' any package $2 and install it to $1.
-PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
-define go-get-tool
-@[ -f $(1) ] || { \
-set -e ;\
-TMP_DIR=$$(mktemp -d) ;\
-cd $$TMP_DIR ;\
-go mod init tmp ;\
-echo "Downloading $(2)" ;\
-GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
-rm -rf $$TMP_DIR ;\
-}
-endef
+##@ Build Dependencies
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+ mkdir -p $(LOCALBIN)
+
+## Tool Binaries
+KUSTOMIZE ?= $(LOCALBIN)/kustomize
+CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+
+## Tool Versions
+KUSTOMIZE_VERSION ?= v3.8.7
+CONTROLLER_TOOLS_VERSION ?= v0.9.0
+
+KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
+$(KUSTOMIZE): $(LOCALBIN)
+ test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); }
+
+.PHONY: controller-gen
+controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
+$(CONTROLLER_GEN): $(LOCALBIN)
+ test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
+
+.PHONY: envtest
+envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
+$(ENVTEST): $(LOCALBIN)
+ test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
```
And then, to make your project use the `kustomize` version defined in the Makefile, replace all usage of `kustomize` with `$(KUSTOMIZE)`
@@ -484,14 +494,14 @@ And then, to make your project use the `kustomize` version defined in the Makefi
Makefile
-You can check all changes applied to the Makefile by looking in the samples projects generated in the `testdata` directory of the Kubebuilder repository or by just by creating a new project with the Kubebuilder CLI.
+You can check all changes applied to the Makefile by looking in the samples projects generated in the `testdata` directory of the Kubebuilder repository or by just by creating a new project with the Kubebuilder CLI.
#### Update your controllers
-
-Controller-runtime version updated has breaking changes
+
+ Controller-runtime version updated has breaking changes
Check [sigs.k8s.io/controller-runtime release docs from 0.7.0+ version][controller-releases] for breaking changes.
@@ -499,22 +509,94 @@ Check [sigs.k8s.io/controller-runtime release docs from 0.7.0+ version][controll
Replace:
-```go
+```go
func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
- ctx := context.Background()
+ ctx := context.Background()
log := r.Log.WithValues("cronjob", req.NamespacedName)
```
With:
-```go
+```go
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("cronjob", req.NamespacedName)
```
+#### Update your controller and webhook test suite
+
+
+ Ginkgo V2 version update has breaking changes
+
+Check [Ginkgo V2 Migration Guide](https://onsi.github.io/ginkgo/MIGRATING_TO_V2) for breaking changes.
+
+
+
+Replace:
+
+```go
+ . "github.com/onsi/ginkgo"
+```
+
+With:
+
+```go
+ . "github.com/onsi/ginkgo/v2"
+```
+
+Also, adjust your test suite.
+
+For Controller Suite:
+
+```go
+ RunSpecsWithDefaultAndCustomReporters(t,
+ "Controller Suite",
+ []Reporter{printer.NewlineReporter{}})
+```
+
+With:
+
+```go
+ RunSpecs(t, "Controller Suite")
+```
+
+For Webhook Suite:
+
+```go
+ RunSpecsWithDefaultAndCustomReporters(t,
+ "Webhook Suite",
+ []Reporter{printer.NewlineReporter{}})
+```
+
+With:
+
+```go
+ RunSpecs(t, "Webhook Suite")
+```
+
+Last but not least, remove the timeout variable from the `BeforeSuite` blocks:
+
+Replace:
+
+```go
+var _ = BeforeSuite(func(done Done) {
+ ....
+}, 60)
+```
+
+With
+
+
+```go
+var _ = BeforeSuite(func(done Done) {
+ ....
+})
+```
+
+
+
#### Change Logger to use flag options
-In the `main.go` file replace:
+In the `main.go` file replace:
```go
flag.Parse()
@@ -531,14 +613,14 @@ opts := zap.Options{
opts.BindFlags(flag.CommandLine)
flag.Parse()
-ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
```
#### Rename the manager flags
-The manager flags `--metrics-addr` and `enable-leader-election` were renamed to `--metrics-bind-address` and `--leader-elect` to be more aligned with core Kubernetes Components. More info: [#1839][issue-1893].
+The manager flags `--metrics-addr` and `enable-leader-election` were renamed to `--metrics-bind-address` and `--leader-elect` to be more aligned with core Kubernetes Components. More info: [#1839][issue-1893].
-In your `main.go` file replace:
+In your `main.go` file replace:
```go
@@ -551,7 +633,7 @@ func main() {
"Enabling this will ensure there is only one active controller manager.")
```
-With:
+With:
```go
func main() {
@@ -595,11 +677,11 @@ The Kubebuilder CLI tool does not support scaffolded resources for both Kubernet
Cert Manager API
-If you scaffold a webhook using the Kubernetes API `admissionregistration.k8s.io/v1` then, by default, it will use the API `cert-manager.io/v1` in the manifests.
+If you scaffold a webhook using the Kubernetes API `admissionregistration.k8s.io/v1` then, by default, it will use the API `cert-manager.io/v1` in the manifests.
-The first step is to update your `PROJECT` file by replacing the `api.crdVersion:v1beta` and `webhooks.WebhookVersion:v1beta` with `api.crdVersion:v1` and `webhooks.WebhookVersion:v1` which would look like:
+The first step is to update your `PROJECT` file by replacing the `api.crdVersion:v1beta` and `webhooks.WebhookVersion:v1beta` with `api.crdVersion:v1` and `webhooks.WebhookVersion:v1` which would look like:
```yaml
domain: my.domain
@@ -621,10 +703,10 @@ version: "3"
You can try to re-create the APIS(CRDs) and Webhooks manifests by using the `--force` flag.
-
-Before re-create
+
+ Before re-create
-Note, however, that the tool will re-scaffold the files which means that you will lose their content.
+Note, however, that the tool will re-scaffold the files which means that you will lose their content.
Before executing the commands ensure that you have the files content stored in another place. An easy option is to use `git` to compare your local change with the previous version to recover the contents.
@@ -634,11 +716,11 @@ Now, re-create the APIS(CRDs) and Webhooks manifests by running the `kubebuilde
[migration-guide-v2-to-v3]: migration_guide_v2tov3.md
-[envtest]: https://book.kubebuilder.io/reference/testing/envtest.html
+[envtest]: https://book.kubebuilder.io/reference/testing/envtest.html
[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/legacy/migration_guide_v1tov2.md b/docs/book/src/migration/legacy/migration_guide_v1tov2.md
index f9f3aefce18..3080ceb6c42 100644
--- a/docs/book/src/migration/legacy/migration_guide_v1tov2.md
+++ b/docs/book/src/migration/legacy/migration_guide_v1tov2.md
@@ -10,7 +10,7 @@ The recommended way to migrate a v1 project is to create a new v2 project and
copy over the API and the reconciliation code. The conversion will end up with a
project that looks like a native v2 project. However, in some cases, it's
possible to do an in-place upgrade (i.e. reuse the v1 project layout, upgrading
-controller-runtime and controller-tools.
+controller-runtime and controller-tools.
Let's take as example an V1 project and migrate it to Kubebuilder
v2. At the end, we should have something that looks like the
@@ -96,7 +96,7 @@ We can replace the `+k8s:deepcopy-gen:interfaces=...` marker (which is
`+kubebuilder:object:root=true`.
We don't need the following markers any more (they're not used anymore, and are
-relics from much older versions of KubeBuilder):
+relics from much older versions of Kubebuilder):
```go
// +genclient
diff --git a/docs/book/src/migration/legacy/migration_guide_v2tov3.md b/docs/book/src/migration/legacy/migration_guide_v2tov3.md
new file mode 100644
index 00000000000..420e20dc8fe
--- /dev/null
+++ b/docs/book/src/migration/legacy/migration_guide_v2tov3.md
@@ -0,0 +1,155 @@
+# Migration from v2 to v3
+
+Make sure you understand the [differences between Kubebuilder v2 and v3][v2vsv3]
+before continuing.
+
+Please ensure you have followed the [installation guide][quick-start]
+to install the required components.
+
+The recommended way to migrate a v2 project is to create a new v3 project and
+copy over the API and the reconciliation code. The conversion will end up with a
+project that looks like a native v3 project. However, in some cases, it's
+possible to do an in-place upgrade (i.e. reuse the v2 project layout, upgrading
+[controller-runtime][controller-runtime] and [controller-tools][controller-tools]).
+
+## Initialize a v3 Project
+
+
+Project name
+
+For the rest of this document, we are going to use `migration-project` as the project name and `tutorial.kubebuilder.io` as the domain. Please, select and use appropriate values for your case.
+
+
+
+Create a new directory with the name of your project. Note that
+this name is used in the scaffolds to create the name of your manager Pod and of the Namespace where the Manager is deployed by default.
+
+```bash
+$ mkdir migration-project-name
+$ cd migration-project-name
+```
+
+Now, we need to initialize a v3 project. Before we do that, though, we'll need
+to initialize a new go module if we're not on the `GOPATH`. While technically this is
+not needed inside `GOPATH`, it is still recommended.
+
+```bash
+go mod init tutorial.kubebuilder.io/migration-project
+```
+
+
+The module of your project can found in the in the `go.mod` file at the root of your project:
+
+```
+module tutorial.kubebuilder.io/migration-project
+```
+
+
+
+Then, we can finish initializing the project with kubebuilder.
+
+```bash
+kubebuilder init --domain tutorial.kubebuilder.io
+```
+
+
+The domain of your project can be found in the PROJECT file:
+
+```yaml
+...
+domain: tutorial.kubebuilder.io
+...
+```
+
+
+## Migrate APIs and Controllers
+
+Next, we'll re-scaffold out the API types and controllers.
+
+
+Scaffolding both the API types and controllers
+
+For this example, we are going to consider that we need to scaffold both the API types and the controllers, but remember that this depends on how you scaffolded them in your original project.
+
+
+
+```bash
+kubebuilder create api --group batch --version v1 --kind CronJob
+```
+
+### Migrate the APIs
+
+
+If you're using multiple groups
+
+Please run `kubebuilder edit --multigroup=true` to enable multi-group support before migrating the APIs and controllers. Please see [this][multi-group] for more details.
+
+
+
+Now, let's copy the API definition from `api/v1/_types.go` in our old project to the new one.
+
+These files have not been modified by the new plugin, so you should be able to replace your freshly scaffolded files by your old one. There may be some cosmetic changes. So you can choose to only copy the types themselves.
+
+### Migrate the Controllers
+
+Now, let's migrate the controller code from `controllers/cronjob_controller.go` in our old project to the new one. There is a breaking change and there may be some cosmetic changes.
+
+The new `Reconcile` method receives the context as an argument now, instead of having to create it with `context.Background()`. You can copy the rest of the code in your old controller to the scaffolded methods replacing:
+
+```go
+func (r *CronJobReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
+ ctx := context.Background()
+ log := r.Log.WithValues("cronjob", req.NamespacedName)
+```
+
+With:
+
+```go
+func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ log := r.Log.WithValues("cronjob", req.NamespacedName)
+```
+
+
+ Controller-runtime version updated has breaking changes
+
+Check [sigs.k8s.io/controller-runtime release docs from 0.8.0+ version][controller-runtime] for breaking changes.
+
+
+
+## Migrate the Webhooks
+
+
+Skip
+
+If you don't have any webhooks, you can skip this section.
+
+
+
+Now let's scaffold the webhooks for our CRD (CronJob). We'll need to run the
+following command with the `--defaulting` and `--programmatic-validation` flags
+(since our test project uses defaulting and validating webhooks):
+
+```bash
+kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
+```
+
+Now, let's copy the webhook definition from `api/v1/_webhook.go` from our old project to the new one.
+
+## Others
+
+If there are any manual updates in `main.go` in v2, we need to port the changes to the new `main.go`. We’ll also need to ensure all of the needed schemes have been registered.
+
+If there are additional manifests added under config directory, port them as well.
+
+Change the image name in the Makefile if needed.
+
+## Verification
+
+Finally, we can run `make` and `make docker-build` to ensure things are working
+fine.
+
+[v2vsv3]: v2vsv3.md
+[quick-start]: /quick-start.md#installation
+[controller-tools]: https://github.com/kubernetes-sigs/controller-tools/releases
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime/releases
+[multi-group]: /migration/multi-group.md
\ No newline at end of file
diff --git a/docs/book/src/migration/v1vsv2.md b/docs/book/src/migration/legacy/v1vsv2.md
similarity index 94%
rename from docs/book/src/migration/v1vsv2.md
rename to docs/book/src/migration/legacy/v1vsv2.md
index f426d88b1d7..993a29845a3 100644
--- a/docs/book/src/migration/v1vsv2.md
+++ b/docs/book/src/migration/legacy/v1vsv2.md
@@ -1,4 +1,4 @@
-# Kubebuilder v1 vs v2
+# Kubebuilder v1 vs v2 (Legacy v1.0.0+ to v2.0.0 Kubebuilder CLI versions)
This document cover all breaking changes when migrating from v1 to v2.
@@ -31,7 +31,7 @@ information.
- Automatic certificate generation for webhooks has been removed, and webhooks
will no longer self-register. Use controller-tools to generate a webhook
configuration. If you need certificate generation, we recommend using
-[cert-manager](https://github.com/jetstack/cert-manager). Kubebuilder v2 will
+[cert-manager](https://github.com/cert-manager/cert-manager). Kubebuilder v2 will
scaffold out cert manager configs for you to use -- see the
[Webhook Tutorial](/cronjob-tutorial/webhook-implementation.md) for more details.
diff --git a/docs/book/src/migration/legacy/v2vsv3.md b/docs/book/src/migration/legacy/v2vsv3.md
new file mode 100644
index 00000000000..2c606b35ed5
--- /dev/null
+++ b/docs/book/src/migration/legacy/v2vsv3.md
@@ -0,0 +1,108 @@
+# Kubebuilder v2 vs v3 (Legacy Kubebuilder v2.0.0+ layout to 3.0.0+)
+
+This document covers all breaking changes when migrating from v2 to v3.
+
+The details of all changes (breaking or otherwise) can be found in
+[controller-runtime][controller-runtime],
+[controller-tools][controller-tools]
+and [kb-releases][kb-releases] release notes.
+
+## Common changes
+
+v3 projects use Go modules and request Go 1.18+. Dep is no longer supported for dependency management.
+
+## Kubebuilder
+
+- Preliminary support for plugins was added. For more info see the [Extensible CLI and Scaffolding Plugins: phase 1][plugins-phase1-design-doc],
+ the [Extensible CLI and Scaffolding Plugins: phase 1.5][plugins-phase1-design-doc-1.5] and the [Extensible CLI and Scaffolding Plugins - Phase 2][plugins-phase2-design-doc]
+ design docs. Also, you can check the [Plugins section][plugins-section].
+
+- The `PROJECT` file now has a new layout. It stores more information about what resources are in use, to better enable plugins to make useful decisions when scaffolding.
+
+ Furthermore, the PROJECT file itself is now versioned: the `version` field corresponds to the version of the PROJECT file itself, while the `layout` field indicates the scaffolding & primary plugin version in use.
+
+- The version of the image `gcr.io/kubebuilder/kube-rbac-proxy`, which is an optional component enabled by default to secure the request made against the manager, was updated from `0.5.0` to `0.11.0` to address security concerns. The details of all changes can be found in [kube-rbac-proxy][kube-rbac-proxy].
+
+## TL;DR of the New `go/v3` Plugin
+
+***More details on this can be found at [here][kb-releases], but for the highlights, check below***
+
+
+Default plugin
+Projects scaffolded with Kubebuilder v3 will use the `go.kubebuilder.io/v3` plugin by default.
+
+
+- Scaffolded/Generated API version changes:
+ * Use `apiextensions/v1` for generated CRDs (`apiextensions/v1beta1` was deprecated in Kubernetes `1.16`)
+ * Use `admissionregistration.k8s.io/v1` for generated webhooks (`admissionregistration.k8s.io/v1beta1` was deprecated in Kubernetes `1.16`)
+ * Use `cert-manager.io/v1` for the certificate manager when webhooks are used (`cert-manager.io/v1alpha2` was deprecated in `Cert-Manager 0.14`. More info: [CertManager v1.0 docs][cert-manager-docs])
+
+- Code changes:
+ * The manager flags `--metrics-addr` and `enable-leader-election` now are named `--metrics-bind-address` and `--leader-elect` to be more aligned with core Kubernetes Components. More info: [#1839][issue-1893]
+ * Liveness and Readiness probes are now added by default using [`healthz.Ping`][healthz-ping].
+ * A new option to create the projects using ComponentConfig is introduced. For more info see its [enhancement proposal][enhancement proposal] and the [Component config tutorial][component-config-tutorial]
+ * Manager manifests now use `SecurityContext` to address security concerns. More info: [#1637][issue-1637]
+- Misc:
+ * Support for [controller-tools][controller-tools] `v0.9.0` (for `go/v2` it is `v0.3.0` and previously it was `v0.2.5`)
+ * Support for [controller-runtime][controller-runtime] `v0.12.1` (for `go/v2` it is `v0.6.4` and previously it was `v0.5.0`)
+ * Support for [kustomize][kustomize] `v3.8.7` (for `go/v2` it is `v3.5.4` and previously it was `v3.1.0`)
+ * Required Envtest binaries are automatically downloaded
+ * The minimum Go version is now `1.18` (previously it was `1.13`).
+
+
+ Project customizations
+
+After using the CLI to create your project, you are free to customise how you see fit. Bear in mind, that it is not recommended to deviate from the proposed layout unless you know what you are doing.
+
+For example, you should refrain from moving the scaffolded files, doing so will make it difficult in upgrading your project in the future. You may also lose the ability to use some of the CLI features and helpers. For further information on the project layout, see the doc [What's in a basic project?][basic-project-doc]
+
+
+
+## Migrating to Kubebuilder v3
+
+So you want to upgrade your scaffolding to use the latest and greatest features then, follow up the following guide which will cover the steps in the most straightforward way to allow you to upgrade your project to get all latest changes and improvements.
+
+
+ Apple Silicon (M1)
+
+The current scaffold done by the CLI (`go/v3`) uses [kubernetes-sigs/kustomize][kustomize] v3 which does not provide
+a valid binary for Apple Silicon (`darwin/arm64`). Therefore, you can use the `go/v4` plugin
+instead which provides support for this platform:
+
+```bash
+kubebuilder init --domain my.domain --repo my.domain/guestbook --plugins=go/v4
+```
+
+
+
+- [Migration Guide v2 to V3][migration-guide-v2-to-v3] **(Recommended)**
+
+### By updating the files manually
+
+So you want to use the latest version of Kubebuilder CLI without changing your scaffolding then, check the following guide which will describe the manually steps required for you to upgrade only your PROJECT version and starts to use the plugins versions.
+
+This way is more complex, susceptible to errors, and success cannot be assured. Also, by following these steps you will not get the improvements and bug fixes in the default generated project files.
+
+You will check that you can still using the previous layout by using the `go/v2` plugin which will not upgrade the [controller-runtime][controller-runtime] and [controller-tools][controller-tools] to the latest version used with `go/v3` becuase of its breaking changes. By checking this guide you know also how to manually change the files to use the `go/v3` plugin and its dependencies versions.
+
+- [Migrating to Kubebuilder v3 by updating the files manually][manually-upgrade]
+
+[plugins-phase1-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1.md
+[plugins-phase1-design-doc-1.5]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1-5.md
+[plugins-phase2-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-2.md
+[plugins-section]: ./../../plugins/plugins.md
+[manually-upgrade]: manually_migration_guide_v2_v3.md
+[component-config-tutorial]: ../../component-config-tutorial/tutorial.md
+[issue-1893]: https://github.com/kubernetes-sigs/kubebuilder/issues/1839
+[migration-guide-v2-to-v3]: migration_guide_v2tov3.md
+[healthz-ping]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/healthz#CheckHandler
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime/releases
+[controller-tools]: https://github.com/kubernetes-sigs/controller-tools/releases
+[kustomize]: https://github.com/kubernetes-sigs/kustomize/releases
+[issue-1637]: https://github.com/kubernetes-sigs/kubebuilder/issues/1637
+[enhancement proposal]: https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/wgs
+[cert-manager-docs]: https://cert-manager.io/docs/installation/upgrading/
+[kb-releases]: https://github.com/kubernetes-sigs/kubebuilder/releases
+[kube-rbac-proxy]: https://github.com/brancz/kube-rbac-proxy/releases
+[basic-project-doc]: ../../cronjob-tutorial/basic-project.md
+[kustomize]: https://github.com/kubernetes-sigs/kustomize
diff --git a/docs/book/src/migration/manually_migration_guide_gov3_to_gov4.md b/docs/book/src/migration/manually_migration_guide_gov3_to_gov4.md
new file mode 100644
index 00000000000..d4cac5a0b4b
--- /dev/null
+++ b/docs/book/src/migration/manually_migration_guide_gov3_to_gov4.md
@@ -0,0 +1,210 @@
+# Migration from go/v3 to go/v4 by updating the files manually
+
+Make sure you understand the [differences between Kubebuilder go/v3 and go/v4][v3vsv4]
+before continuing.
+
+Please ensure you have followed the [installation guide][quick-start]
+to install the required components.
+
+The following guide describes the manual steps required to upgrade your PROJECT config file to begin using `go/v4`.
+
+This way is more complex, susceptible to errors, and success cannot be assured. Also, by following these steps you will not get the improvements and bug fixes in the default generated project files.
+
+Usually it is suggested to do it manually if you have customized your project and deviated too much from the proposed scaffold. Before continuing, ensure that you understand the note about [project customizations][project-customizations]. Note that you might need to spend more effort to do this process manually than to organize your project customizations. The proposed layout will keep your project maintainable and upgradable with less effort in the future.
+
+The recommended upgrade approach is to follow the [Migration Guide go/v3 to go/v4][migration-guide-gov3-to-gov4] instead.
+
+## Migration from project config version "go/v3" to "go/v4"
+
+Update the `PROJECT` file layout which stores information about the resources that are used to enable plugins make
+useful decisions while scaffolding. The `layout` field indicates the scaffolding and the primary plugin version in use.
+
+### Steps to migrate
+
+#### Migrate the layout version into the PROJECT file
+
+The following steps describe the manual changes required to bring the project configuration file (`PROJECT`).
+These change will add the information that Kubebuilder would add when generating the file. This file can be found in the root directory.
+
+Update the PROJECT file by replacing:
+
+```yaml
+layout:
+- go.kubebuilder.io/v3
+```
+
+With:
+
+```yaml
+layout:
+- go.kubebuilder.io/v4
+
+```
+
+#### Changes to the layout
+
+##### New layout:
+
+- The directory `apis` was renamed to `api` to follow the standard
+- The `controller(s)` directory has been moved under a new directory called `internal` and renamed to singular as well `controller`
+- The `main.go` previously scaffolded in the root directory has been moved under a new directory called `cmd`
+
+Therefore, you can check the changes in the layout results into:
+
+```sh
+...
+├── cmd
+│ └── main.go
+├── internal
+│ └── controller
+└── api
+```
+
+##### Migrating to the new layout:
+
+- Create a new directory `cmd` and move the `main.go` under it.
+- If your project support multi-group the APIs are scaffold under a directory called `apis`. Rename this directory to `api`
+- Move the `controllers` directory under the `internal` and rename it for `controller`
+- Now ensure that the imports will be updated accordingly by:
+ - Update the `main.go` imports to look for the new path of your controllers under the `internal/controller` directory
+
+**Then, let's update the scaffolds paths**
+
+- Update the Dockerfile to ensure that you will have:
+
+```
+COPY cmd/main.go cmd/main.go
+COPY api/ api/
+COPY internal/controller/ internal/controller/
+```
+
+Then, replace:
+
+```
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go
+
+```
+
+With:
+
+```
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
+```
+
+- Update the Makefile targets to build and run the manager by replacing:
+
+```
+.PHONY: build
+build: manifests generate fmt vet ## Build manager binary.
+ go build -o bin/manager main.go
+
+.PHONY: run
+run: manifests generate fmt vet ## Run a controller from your host.
+ go run ./main.go
+```
+
+With:
+
+```
+.PHONY: build
+build: manifests generate fmt vet ## Build manager binary.
+ go build -o bin/manager cmd/main.go
+
+.PHONY: run
+run: manifests generate fmt vet ## Run a controller from your host.
+ go run ./cmd/main.go
+```
+
+- Update the `internal/controller/suite_test.go` to set the path for the `CRDDirectoryPaths`:
+
+Replace:
+
+```
+CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
+```
+
+With:
+
+```
+CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
+```
+
+Note that if your project has multiple groups (`multigroup:true`) then the above update should result into `"..", "..", "..",` instead of `"..",".."`
+
+#### Now, let's update the PATHs in the PROJECT file accordingly
+
+The PROJECT tracks the paths of all APIs used in your project. Ensure that they now point to `api/...` as the following example:
+
+**Before update:**
+```
+ group: crew
+ kind: Captain
+ path: sigs.k8s.io/kubebuilder/testdata/project-v4/apis/crew/v1
+```
+
+**After Update:**
+```
+
+ group: crew
+ kind: Captain
+ path: sigs.k8s.io/kubebuilder/testdata/project-v4/api/crew/v1
+```
+
+### Update kustomize manifests with the changes made so far
+
+- Update the manifest under `config/` directory with all changes performed in the default scaffold done with `go/v4` plugin. (see for example `testdata/project-v4/config/`) to get all changes in the
+ default scaffolds to be applied on your project
+- Create `config/samples/kustomization.yaml` with all Custom Resources samples specified into `config/samples`. _(see for example `testdata/project-v4/config/samples/kustomization.yaml`)_
+
+
+`config/` directory with changes into the scaffold files
+
+Note that under the `config/` directory you will find scaffolding changes since using
+`go/v4` you will ensure that you are no longer using Kustomize v3x.
+
+You can mainly compare the `config/` directory from the samples scaffolded under the `testdata`directory by
+checking the differences between the `testdata/project-v3/config/` with `testdata/project-v4/config/` which
+are samples created with the same commands with the only difference being versions.
+
+However, note that if you create your project with Kubebuilder CLI 3.0.0, its scaffolds
+might change to accommodate changes up to the latest releases using `go/v3` which are not considered
+breaking for users and/or are forced by the changes introduced in the dependencies
+used by the project such as [controller-runtime][controller-runtime] and [controller-tools][controller-tools].
+
+
+
+### If you have webhooks:
+
+Replace the import `admissionv1beta1 "k8s.io/api/admission/v1beta1"` with `admissionv1 "k8s.io/api/admission/v1"` in the webhook test files
+
+### Makefile updates
+
+Update the Makefile with the changes which can be found in the samples under testdata for the release tag used. (see for example `testdata/project-v4/Makefile`)
+
+### Update the dependencies
+
+Update the `go.mod` with the changes which can be found in the samples under `testdata` for the release tag used. (see for example `testdata/project-v4/go.mod`). Then, run
+`go mod tidy` to ensure that you get the latest dependencies and your Golang code has no breaking changes.
+
+### Verification
+
+In the steps above, you updated your project manually with the goal of ensuring that it follows
+the changes in the layout introduced with the `go/v4` plugin that update the scaffolds.
+
+There is no option to verify that you properly updated the `PROJECT` file of your project.
+The best way to ensure that everything is updated correctly, would be to initialize a project using the `go/v4` plugin,
+(ie) using `kubebuilder init --domain tutorial.kubebuilder.io plugins=go/v4` and generating the same API(s),
+controller(s), and webhook(s) in order to compare the generated configuration with the manually changed configuration.
+
+Also, after all updates you would run the following commands:
+
+- `make manifests` (to re-generate the files using the latest version of the controller-gen after you update the Makefile)
+- `make all` (to ensure that you are able to build and perform all operations)
+
+[v3vsv4]: v3vsv4.md
+[quick-start]: ./../quick-start.md#installation
+[migration-guide-gov3-to-gov4]: migration_guide_gov3_to_gov4.md
+[controller-tools]: https://github.com/kubernetes-sigs/controller-tools/releases
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime/releases
+[multi-group]: multi-group.md
+
diff --git a/docs/book/src/migration/migration_guide_gov3_to_gov4.md b/docs/book/src/migration/migration_guide_gov3_to_gov4.md
new file mode 100644
index 00000000000..8ccd94de4f9
--- /dev/null
+++ b/docs/book/src/migration/migration_guide_gov3_to_gov4.md
@@ -0,0 +1,161 @@
+# Migration from go/v3 to go/v4
+
+Make sure you understand the [differences between Kubebuilder go/v3 and go/v4][v3vsv4]
+before continuing.
+
+Please ensure you have followed the [installation guide][quick-start]
+to install the required components.
+
+The recommended way to migrate a `go/v3` project is to create a new `go/v4` project and
+copy over the API and the reconciliation code. The conversion will end up with a
+project that looks like a native go/v4 project layout (latest version).
+
+
+ Your Upgrade Assistant: The `alpha generate` command
+
+To upgrade your project you might want to use the command `kubebuilder alpha generate [OPTIONS]`.
+This command will re-scaffold the project using the current Kubebuilder version.
+You can run `kubebuilder alpha generate --plugins=go/v4` to regenerate your project using `go/v4`
+based in your [PROJECT][project-file] file config. ([More info](./../reference/rescaffold.md))
+
+
+
+However, in some cases, it's possible to do an in-place upgrade (i.e. reuse the go/v3 project layout, upgrading
+the PROJECT file, and scaffolds manually). For further information see [Migration from go/v3 to go/v4 by updating the files manually][manually-upgrade]
+
+## Initialize a go/v4 Project
+
+
+Project name
+
+For the rest of this document, we are going to use `migration-project` as the project name and `tutorial.kubebuilder.io` as the domain. Please, select and use appropriate values for your case.
+
+
+
+Create a new directory with the name of your project. Note that
+this name is used in the scaffolds to create the name of your manager Pod and of the Namespace where the Manager is deployed by default.
+
+```bash
+$ mkdir migration-project-name
+$ cd migration-project-name
+```
+
+Now, we need to initialize a go/v4 project. Before we do that, we'll need
+to initialize a new go module if we're not on the `GOPATH`. While technically this is
+not needed inside `GOPATH`, it is still recommended.
+
+```bash
+go mod init tutorial.kubebuilder.io/migration-project
+```
+
+
+The module of your project can found in the `go.mod` file at the root of your project:
+
+```
+module tutorial.kubebuilder.io/migration-project
+```
+
+
+
+Now, we can finish initializing the project with kubebuilder.
+
+```bash
+kubebuilder init --domain tutorial.kubebuilder.io --plugins=go/v4
+```
+
+
+The domain of your project can be found in the PROJECT file:
+
+```yaml
+...
+domain: tutorial.kubebuilder.io
+...
+```
+
+
+## Migrate APIs and Controllers
+
+Next, we'll re-scaffold out the API types and controllers.
+
+
+Scaffolding both the API types and controllers
+
+For this example, we are going to consider that we need to scaffold both the API types and the controllers, but remember that this depends on how you scaffolded them in your original project.
+
+
+
+```bash
+kubebuilder create api --group batch --version v1 --kind CronJob
+```
+
+### Migrate the APIs
+
+
+If you're using multiple groups
+
+Please run `kubebuilder edit --multigroup=true` to enable multi-group support before migrating the APIs and controllers. Please see [this][multi-group] for more details.
+
+
+
+Now, let's copy the API definition from `api/v1/_types.go` in our old project to the new one.
+
+These files have not been modified by the new plugin, so you should be able to replace your freshly scaffolded files by your old one. There may be some cosmetic changes. So you can choose to only copy the types themselves.
+
+### Migrate the Controllers
+
+Now, let's migrate the controller code from `controllers/cronjob_controller.go` in our old project to `internal/controller/cronjob_controller.go` in the new one.
+
+## Migrate the Webhooks
+
+
+Skip
+
+If you don't have any webhooks, you can skip this section.
+
+
+
+Now let's scaffold the webhooks for our CRD (CronJob). We'll need to run the
+following command with the `--defaulting` and `--programmatic-validation` flags
+(since our test project uses defaulting and validating webhooks):
+
+```bash
+kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
+```
+
+Now, let's copy the webhook definition from `api/v1/_webhook.go` from our old project to the new one.
+
+## Others
+
+If there are any manual updates in `main.go` in v3, we need to port the changes to the new `main.go`. We'll also need to ensure all of needed controller-runtime `schemes` have been registered.
+
+If there are additional manifests added under config directory, port them as well. Please, be aware that
+the new version go/v4 uses Kustomize v5x and no longer Kustomize v4. Therefore, if added customized
+implementations in the config you need to ensure that they can work with Kustomize v5 and if not
+update/upgrade any breaking change that you might face.
+
+In v4, installation of Kustomize has been changed from bash script to `go get`. Change the `kustomize` dependency in Makefile to
+```
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
+$(KUSTOMIZE): $(LOCALBIN)
+ @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \
+ echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \
+ rm -rf $(LOCALBIN)/kustomize; \
+ fi
+ test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION)
+```
+
+Change the image name in the Makefile if needed.
+
+## Verification
+
+Finally, we can run `make` and `make docker-build` to ensure things are working
+fine.
+
+[v3vsv4]: v3vsv4.md
+[quick-start]: ./../quick-start.md#installation
+[controller-tools]: https://github.com/kubernetes-sigs/controller-tools/releases
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime/releases
+[multi-group]: multi-group.md
+[manually-upgrade]: manually_migration_guide_gov3_to_gov4.md
+[project-file]: ../reference/project-config.md
diff --git a/docs/book/src/migration/migration_guide_v2tov3.md b/docs/book/src/migration/migration_guide_v2tov3.md
deleted file mode 100644
index be1a1d2dcbd..00000000000
--- a/docs/book/src/migration/migration_guide_v2tov3.md
+++ /dev/null
@@ -1,184 +0,0 @@
-# Migration from v2 to v3
-
-Make sure you understand the [differences between Kubebuilder v2 and v3][v2vsv3]
-before continuing.
-
-Please ensure you have followed the [installation guide][quick-start]
-to install the required components.
-
-The recommended way to migrate a v2 project is to create a new v3 project and
-copy over the API and the reconciliation code. The conversion will end up with a
-project that looks like a native v3 project. However, in some cases, it's
-possible to do an in-place upgrade (i.e. reuse the v2 project layout, upgrading
-[controller-runtime][controller-runtime] and [controller-tools][controller-tools]).
-
-## Initialize a v3 Project
-
-
-Project name
-
-For the rest of this document, we are going to use `migration-project` as the project name and `tutorial.kubebuilder.io` as the domain. Please, select and use appropriate values for your case.
-
-
-
-Create a new directory with the name of your project. Note that
-this name is used in the scaffolds to create the name of your manager Pod and of the Namespace where the Manager is deployed by default.
-
-```bash
-$ mkdir migration-project-name
-$ cd migration-project-name
-```
-
-Now, we need to initialize a v3 project. Before we do that, though, we'll need
-to initialize a new go module if we're not on the `GOPATH`. While technically this is
-not needed inside `GOPATH`, it is still recommended.
-
-```bash
-go mod init tutorial.kubebuilder.io/migration-project
-```
-
- Migrating to Kubebuilder v3 while staying on the go/v2 plugin
-
-You can use `--plugins=go/v2` if you wish to continue using "`Kubebuilder 2.x`" layout and avoid dealing with the breaking changes that will be faced because of the default upper versions which will be used now. See that the [controller-tools][controller-tools] `v0.4.1` & [controller-runtime][controller-runtime] `v0.7.0` are just used by default with the `go/v3` plugin layout.
-
-
-
-The module of your project can found in the in the `go.mod` file at the root of your project:
-
-```
-module tutorial.kubebuilder.io/migration-project
-```
-
-
-
-Then, we can finish initializing the project with kubebuilder.
-
-```bash
-kubebuilder init --domain tutorial.kubebuilder.io
-```
-
-
-The domain of your project can be found in the PROJECT file:
-
-```yaml
-...
-domain: tutorial.kubebuilder.io
-...
-```
-
-
-## Migrate APIs and Controllers
-
-Next, we'll re-scaffold out the API types and controllers.
-
-
-Scaffolding both the API types and controllers
-
-For this example, we are going to consider that we need to scaffold both the API types and the controllers, but remember that this depends on how you scaffolded them in your original project.
-
-
-
-```bash
-kubebuilder create api --group batch --version v1 --kind CronJob
-```
-
-
-How to still keep `apiextensions.k8s.io/v1beta1` for CRDs?
-
-From now on, the CRDs that will be created by controller-gen will be using the Kubernetes API version `apiextensions.k8s.io/v1` by default, instead of `apiextensions.k8s.io/v1beta1`.
-
-The `apiextensions.k8s.io/v1beta1` was deprecated in Kubernetes `1.16` and will be removed in Kubernetes `1.22`.
-
-So, if you would like to keep using the previous version use the flag `--crd-version=v1beta1` in the above command which is only needed if you want your operator to support Kubernetes `1.15` and earlier.
-
-
-
-### Migrate the APIs
-
-
-If you're using multiple groups
-
-Please run `kubebuilder edit --multigroup=true` to enable multi-group support before migrating the APIs and controllers. Please see [this][multi-group] for more details.
-
-
-
-Now, let's copy the API definition from `api/v1/_types.go` in our old project to the new one.
-
-These files have not been modified by the new plugin, so you should be able to replace your freshly scaffolded files by your old one. There may be some cosmetic changes. So you can choose to only copy the types themselves.
-
-### Migrate the Controllers
-
-Now, let's migrate the controller code from `controllers/cronjob_controller.go` in our old project to the new one. There is a breaking change and there may be some cosmetic changes.
-
-The new `Reconcile` method receives the context as an argument now, instead of having to create it with `context.Background()`. You can copy the rest of the code in your old controller to the scaffolded methods replacing:
-
-```go
-func (r *CronJobReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
- ctx := context.Background()
- log := r.Log.WithValues("cronjob", req.NamespacedName)
-```
-
-With:
-
-```go
-func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- log := r.Log.WithValues("cronjob", req.NamespacedName)
-```
-
-
-Controller-runtime version updated has breaking changes
-
-Check [sigs.k8s.io/controller-runtime release docs from 0.7.0+ version][controller-runtime] for breaking changes.
-
-
-
-## Migrate the Webhooks
-
-
-Skip
-
-If you don't have any webhooks, you can skip this section.
-
-
-
-Now let's scaffold the webhooks for our CRD (CronJob). We'll need to run the
-following command with the `--defaulting` and `--programmatic-validation` flags
-(since our test project uses defaulting and validating webhooks):
-
-```bash
-kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
-```
-
-
-How to keep using `apiextensions.k8s.io/v1beta1` for Webhooks?
-
-From now on, the Webhooks that will be created by Kubebuilder using by default the Kubernetes API version `admissionregistration.k8s.io/v1` instead of `admissionregistration.k8s.io/v1beta1` and the `cert-manager.io/v1` to replace `cert-manager.io/v1alpha2`.
-
-Note that `apiextensions/v1beta1` and `admissionregistration.k8s.io/v1beta1` were deprecated in Kubernetes `1.16` and will be removed in Kubernetes `1.22`. If you use `apiextensions/v1` and `admissionregistration.k8s.io/v1` then you need to use `cert-manager.io/v1` which will be the API adopted per Kubebuilder CLI by default in this case.
-
-The API `cert-manager.io/v1alpha2` is not compatible with the latest Kubernetes API versions.
-
-So, if you would like to keep using the previous version use the flag `--webhook-version=v1beta1` in the above command which is only needed if you want your operator to support Kubernetes `1.15` and earlier.
-
-
-
-Now, let's copy the webhook definition from `api/v1/_webhook.go` from our old project to the new one.
-
-## Others
-
-If there are any manual updates in `main.go` in v2, we need to port the changes to the new `main.go`. We’ll also need to ensure all of the needed schemes have been registered.
-
-If there are additional manifests added under config directory, port them as well.
-
-Change the image name in the Makefile if needed.
-
-## Verification
-
-Finally, we can run `make` and `make docker-build` to ensure things are working
-fine.
-
-[v2vsv3]: ./v2vsv3.md
-[quick-start]: /quick-start.md#installation
-[controller-tools]: https://github.com/kubernetes-sigs/controller-tools/releases
-[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime/releases
-[multi-group]: /migration/multi-group.md
\ No newline at end of file
diff --git a/docs/book/src/migration/multi-group.md b/docs/book/src/migration/multi-group.md
index 95a80543826..fdc3fd5d551 100644
--- a/docs/book/src/migration/multi-group.md
+++ b/docs/book/src/migration/multi-group.md
@@ -1,25 +1,35 @@
# Single Group to Multi-Group
-
+
-Note
+Note
-Multi-group scaffolding support was not present in the initial version of
-the KubeBuilder v2 scaffolding (as of KubeBuilder v2.0.0).
+While Kubebuilder will not scaffold out a project structure compatible
+with multiple API groups in the same repository by default, it's possible
+to modify the default project structure to support it.
-To change the layout of your project to support Multi-Group run the command
-`kubebuilder edit --multigroup=true`. Once you switch to a multi-group layout, the new Kinds
-will be generated in the new layout but additional manual work is needed
-to move the old API groups to the new layout.
+Note that the process mainly is to ensure that your API(s) and controller(s) will be moved under new directories with their respective group name.
-While KubeBuilder v2 will not scaffold out a project structure compatible
-with multiple API groups in the same repository by default, it's possible
-to modify the default project structure to support it.
-
Let's migrate the [CronJob example][cronjob-tutorial].
+
+ Instructions vary per project layout
+
+You can verify the version by looking at the PROJECT file. The currently default and
+recommended version is go/v4.
+
+The layout go/v3 is **deprecated**, if you are using go/v3 it is recommended that you migrate to
+go/v4, however this documentation is still valid. [Migration from go/v3 to go/v4][migration-guide].
+
+
+
+To change the layout of your project to support Multi-Group run the command
+`kubebuilder edit --multigroup=true`. Once you switch to a multi-group layout, the new Kinds
+will be generated in the new layout but additional manual work is needed
+to move the old API groups to the new layout.
+
Generally, we use the prefix for the API group as the directory name. We
can check `api/v1/groupversion_info.go` to find that out:
@@ -28,56 +38,83 @@ can check `api/v1/groupversion_info.go` to find that out:
package v1
```
-Then, we'll rename `api` to `apis` to be more clear, and we'll move our
-existing APIs into a new subdirectory, "batch":
+Then, we'll move our existing APIs into a new subdirectory named after the group. Considering the [CronJob example][cronjob-tutorial], subdirectory name is "batch":
+
+```bash
+mkdir api/batch
+mv api/* api/batch
+```
+
+After moving the APIs to a new directory, the same needs to be applied to the controllers. For go/v4:
```bash
-mkdir apis/batch
-mv api/* apis/batch
-# After ensuring that all was moved successfully remove the old directory `api/`
-rm -rf api/
+mkdir internal/controller/batch
+mv internal/controller/* internal/controller/batch/
```
+The same needs to be applied for any pre-existing [webhooks][webhooks]:
+```bash
+mkdir internal/webhook/batch
+mv internal/webhook/* internal/webhook/batch/
+```
+For any new webhook created for a new group, the respective functions will be created under subdirectory `internal/webhook//`.
-After moving the APIs to a new directory, the same needs to be applied to the controllers:
+
+If you are using the deprecated layout go/v3
+Then, your layout has not the internal directory. So, you will move the controller(s) under a directory with the name of the API group which it is responsible for manage.
```bash
-mkdir controllers/batch
-mv controllers/* controllers/batch/
+mkdir controller/batch
+mv controller/* controller/batch/
```
-Next, we'll need to update all the references to the old package name.
-For CronJob, that'll be `main.go` and `controllers/batch/cronjob_controller.go`.
+Next, we'll need to update all the references to the old package name.
+For CronJob, the paths to be replaced would be `main.go` and `controllers/batch/cronjob_controller.go` to their respective locations in the new project structure.
If you've added additional files to your project, you'll need to track down
imports there as well.
-Finally, we'll run the command which enable the multi-group layout in the project:
+Finally, fix the PROJECT file manually, the command `kubebuilder edit --multigroup=true`
+sets our project to multigroup, but it doesn't fix the path of the existing APIs.
+For each resource we need to modify the path.
-```
-kubebuilder edit --multigroup=true
-```
+For instance, for a file:
-When the command `kubebuilder edit --multigroup=true` is executed it will add a new line
-to `PROJECT` that marks this a multi-group project:
-
```yaml
-version: "2"
+# Code generated by tool. DO NOT EDIT.
+# This file is used to track the info used to scaffold your project
+# and allow the plugins properly work.
+# More info: https://book.kubebuilder.io/reference/project-config.html
domain: tutorial.kubebuilder.io
-repo: tutorial.kubebuilder.io/project
+layout:
+- go.kubebuilder.io/v4
multigroup: true
+projectName: test
+repo: tutorial.kubebuilder.io/project
+resources:
+- api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: tutorial.kubebuilder.io
+ group: batch
+ kind: CronJob
+ path: tutorial.kubebuilder.io/project/api/v1beta1
+ version: v1beta1
+version: "3"
```
-Note that this option indicates to KubeBuilder that this is a multi-group project.
+Replace `path: tutorial.kubebuilder.io/project/api/v1beta1` for
+`path: tutorial.kubebuilder.io/project/api/batch/v1beta1`
+
+In this process, if the project is not new and has previous implemented APIs they would still need to be modified as needed.
+Notice that with the `multi-group` project the Kind API's files are created under `api//` instead of `api/`.
+Also, note that the controllers will be created under `internal/controller/` instead of `internal/controller`.
-In this way, if the project is not new and has previous APIs already implemented will be in the previous structure.
-Notice that with the `multi-group` project the Kind API's files are
-created under `apis//` instead of `api/`.
-Also, note that the controllers will be created under `controllers/` instead of `controllers`.
-That is the reason why we moved the previously generated APIs with the provided scripts in the previous steps.
-Remember to update the references afterwards.
+That is the reason why we moved the previously generated APIs to their respective locations in the new structure.
+Remember to update the references in imports accordingly.
-For envtest to install CRDs correctly into the test environment, the relative path to the CRD directory needs to be updated accordingly in each `controllers//suite_test.go` file. We need to add additional `".."` to our CRD directory relative path as shown below.
+For envtest to install CRDs correctly into the test environment, the relative path to the CRD directory needs to be updated accordingly in each `internal/controller//suite_test.go` file. We need to add additional `".."` to our CRD directory relative path as shown below.
```go
By("bootstrapping test environment")
@@ -87,8 +124,10 @@ For envtest to install CRDs correctly into the test environment, the relative pa
```
The [CronJob tutorial][cronjob-tutorial] explains each of these changes in
-more detail (in the context of how they're generated by KubeBuilder for
+more detail (in the context of how they're generated by Kubebuilder for
single-group projects).
-[multi-group-issue]: https://github.com/kubernetes-sigs/kubebuilder/issues/923 "KubeBuilder Issue #923"
+[multi-group-issue]: https://github.com/kubernetes-sigs/kubebuilder/issues/923 "Kubebuilder Issue #923"
[cronjob-tutorial]: /cronjob-tutorial/cronjob-tutorial.md "Tutorial: Building CronJob"
+[migration-guide]: /migration/migration_guide_gov3_to_gov4.md "Migration from go/v3 to go/v4"
+[webhooks]: /cronjob-tutorial/webhook-implementation.md "Implementing defaulting/validating webhooks"
diff --git a/docs/book/src/migration/v2vsv3.md b/docs/book/src/migration/v2vsv3.md
deleted file mode 100644
index 44c8c6dfb18..00000000000
--- a/docs/book/src/migration/v2vsv3.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Kubebuilder v2 vs v3
-
-This document covers all breaking changes when migrating from v2 to v3.
-
-The details of all changes (breaking or otherwise) can be found in
-[controller-runtime][controller-runtime],
-[controller-tools][controller-tools]
-and [kb-releases][kb-releases] release notes.
-
-## Common changes
-
-v3 projects use Go modules and request Go 1.15+. Dep is no longer supported for dependency management.
-
-## Kubebuilder
-
-- Preliminary support for plugins was added. For more info see the [Extensible CLI and Scaffolding Plugins: phase 1][plugins-phase1-design-doc] and [Extensible CLI and Scaffolding Plugins: phase 1.5][plugins-phase1-design-doc-1.5]
-
-- The `PROJECT` file now has a new layout. It stores more information about what resources are in use, to better enable plugins to make useful decisions when scaffolding.
-
- Furthermore, the PROJECT file itself is now versioned: the `version` field corresponds to the version of the PROJECT file itself, while the `layout` field indicates the scaffolding & primary plugin version in use.
-
-- The version of the image `gcr.io/kubebuilder/kube-rbac-proxy`, which is an optional component enabled by default to secure the request made against the manager, was updated from `0.5.0` to `0.8.0` to address security concerns. The details of all changes can be found in [kube-rbac-proxy][kube-rbac-proxy].
-
-## TL;DR of the New `go/v3` Plugin
-
-***More details on this can be found at [here][kb-releases], but for the highlights, check below***
-
-
-Default plugin
-Projects scaffolded with Kubebuilder v3 will use the `go.kubebuilder.io/v3` plugin by default.
-
-
-- Scaffolded/Generated API version changes:
- * Use `apiextensions/v1` for generated CRDs (`apiextensions/v1beta1` was deprecated in Kubernetes `1.16`)
- * Use `admissionregistration.k8s.io/v1` for generated webhooks (`admissionregistration.k8s.io/v1beta1` was deprecated in Kubernetes `1.16`)
- * Use `cert-manager.io/v1` for the certificate manager when webhooks are used (`cert-manager.io/v1alpha2` was deprecated in `Cert-Manager 0.14`. More info: [CertManager v1.0 docs][cert-manager-docs])
-
-- Code changes:
- * The manager flags `--metrics-addr` and `enable-leader-election` now are named `--metrics-bind-address` and `--leader-elect` to be more aligned with core Kubernetes Components. More info: [#1839][issue-1893]
- * Liveness and Readiness probes are now added by default using [`healthz.Ping`][healthz-ping].
- * A new option to create the projects using ComponentConfig is introduced. For more info see its [enhancement proposal][enhancement proposal] and the [Component config tutorial][component-config-tutorial]
- * Manager manifests now use `SecurityContext` to address security concerns. More info: [#1637][issue-1637]
-- Misc:
- * Support for [controller-tools][controller-tools] `v0.4.1` (for `go/v2` it is `v0.3.0` and previously it was `v0.2.5`)
- * Support for [controller-runtime][controller-runtime] `v0.7.0` (for `go/v2` it is `v0.6.4` and previously it was `v0.5.0`)
- * Support for [kustomize][kustomize] `v3.8.7` (for `go/v2` it is `v3.5.4` and previously it was `v3.1.0`)
- * Required Envtest binaries are automatically downloaded
- * The minimum Go version is now `1.15` (previously it was `1.13).
-
-
-Project customizations
-
-After using the CLI to create your project, you are free to customise how you see fit. Bear in mind, that it is not recommended to deviate from the proposed layout unless you know what you are doing.
-
-For example, you should refrain from moving the scaffolded files, doing so will make it difficult in upgrading your project in the future. You may also lose the ability to use some of the CLI features and helpers. For further information on the project layout, see the doc [What's in a basic project?][basic-project-doc]
-
-
-
-## Migrating to Kubebuilder v3
-
-So you want to upgrade your scaffolding to use the latest and greatest features then, follow up the following guide which will cover the steps in the most straightforward way to allow you to upgrade your project to get all latest changes and improvements.
-
-- [Migration Guide v2 to V3][migration-guide-v2-to-v3] **(Recommended)**
-
-### By updating the files manually
-
-So you want to use the latest version of Kubebuilder CLI without changing your scaffolding then, check the following guide which will describe the manually steps required for you to upgrade only your PROJECT version and starts to use the plugins versions.
-
-This way is more complex, susceptible to errors, and success cannot be assured. Also, by following these steps you will not get the improvements and bug fixes in the default generated project files.
-
-You will check that you can still using the previous layout by using the `go/v2` plugin which will not upgrade the [controller-runtime][controller-runtime] and [controller-tools][controller-tools] to the latest version used with `go/v3` becuase of its breaking changes. By checking this guide you know also how to manually change the files to use the `go/v3` plugin and its dependencies versions.
-
-- [Migrating to Kubebuilder v3 by updating the files manually][manually-upgrade]
-
-[plugins-phase1-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1.md
-[plugins-phase1-design-doc-1.5]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1-5.md
-[manually-upgrade]: manually_migration_guide_v2_v3.md
-[component-config-tutorial]: ../component-config-tutorial/tutorial.md
-[issue-1893]: https://github.com/kubernetes-sigs/kubebuilder/issues/1839
-[migration-guide-v2-to-v3]: migration_guide_v2tov3.md
-[healthz-ping]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/healthz#CheckHandler
-[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime/releases
-[controller-tools]: https://github.com/kubernetes-sigs/controller-tools/releases
-[kustomize]: https://github.com/kubernetes-sigs/kustomize/releases
-[issue-1637]: https://github.com/kubernetes-sigs/kubebuilder/issues/1637
-[enhancement proposal]: https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/wgs
-[cert-manager-docs]: https://cert-manager.io/docs/installation/upgrading/
-[kb-releases]: https://github.com/kubernetes-sigs/kubebuilder/releases
-[kube-rbac-proxy]: https://github.com/brancz/kube-rbac-proxy/releases
-[basic-project-doc]: ../cronjob-tutorial/basic-project.md
\ No newline at end of file
diff --git a/docs/book/src/migration/v3-plugins.md b/docs/book/src/migration/v3-plugins.md
new file mode 100644
index 00000000000..c04e9d77c8b
--- /dev/null
+++ b/docs/book/src/migration/v3-plugins.md
@@ -0,0 +1,8 @@
+# V3 - Plugins Layout Migration Guides
+
+Following the migration guides from the plugins versions. Note that the plugins ecosystem
+was introduced with Kubebuilder v3.0.0 release where the go/v3 version is the default layout
+since `28 Apr 2021`.
+
+Therefore, you can check here how to migrate the projects built from Kubebuilder 3.x with
+the plugin go/v3 to the latest.
diff --git a/docs/book/src/migration/v3vsv4.md b/docs/book/src/migration/v3vsv4.md
new file mode 100644
index 00000000000..4e32da8b903
--- /dev/null
+++ b/docs/book/src/migration/v3vsv4.md
@@ -0,0 +1,75 @@
+# go/v3 vs go/v4
+
+This document covers all breaking changes when migrating from projects built using the plugin go/v3 (default for any scaffold done since `28 Apr 2021`) to the next version of the Golang plugin `go/v4`.
+
+The details of all changes (breaking or otherwise) can be found in:
+
+- [controller-runtime][controller-runtime]
+- [controller-tools][controller-tools]
+- [kustomize][kustomize-release]
+- [kb-releases][kb-releases] release notes.
+
+## Common changes
+
+- `go/v4` projects use Kustomize v5x (instead of v3x)
+- note that some manifests under `config/` directory have been changed in order to no longer use the deprecated Kustomize features
+ such as env vars.
+- A `kustomization.yaml` is scaffolded under `config/samples`. This helps simply and flexibly generate sample manifests: `kustomize build config/samples`.
+- adds support for Apple Silicon M1 (darwin/arm64)
+- remove support to CRD/WebHooks Kubernetes API v1beta1 version which are no longer supported since k8s 1.22
+- no longer scaffold webhook test files with `"k8s.io/api/admission/v1beta1"` the k8s API which is no longer served since k8s `1.25`. By default
+ webhooks test files are scaffolding using `"k8s.io/api/admission/v1"` which is support from k8s `1.20`
+- no longer provide backwards compatible support with k8s versions < `1.16`
+- change the layout to accommodate the community request to follow the [Standard Go Project Layout][standard-go-project]
+by moving the api(s) under a new directory called `api`, controller(s) under a new directory called `internal` and the `main.go` under a new directory named `cmd`
+
+
+ Details of go/v4 Plugin
+
+Further details can be found in the [go/v4 plugin section][go/v4-doc]
+
+
+
+## TL;DR of the New `go/v4` Plugin
+
+**_More details on this can be found at [here][kb-releases], but for the highlights, check below_**
+
+
+ Project customizations
+
+After using the CLI to create your project, you are free to customize how you see fit. Bear in mind, that it is not recommended to deviate from the proposed layout unless you know what you are doing.
+
+For example, you should refrain from moving the scaffolded files, doing so will make it difficult in upgrading your project in the future. You may also lose the ability to use some of the CLI features and helpers. For further information on the project layout, see the doc [What's in a basic project?][basic-project-doc]
+
+
+
+## Migrating to Kubebuilder go/v4
+
+If you want to upgrade your scaffolding to use the latest and greatest features then, follow the guide
+which will cover the steps in the most straightforward way to allow you to upgrade your project to get all
+latest changes and improvements.
+
+- [Migration Guide go/v3 to go/v4][migration-guide-gov3-to-gov4] **(Recommended)**
+
+### By updating the files manually
+
+If you want to use the latest version of Kubebuilder CLI without changing your scaffolding then, check the following guide which will describe the steps to be performed manually to upgrade only your PROJECT version and start using the plugins versions.
+
+This way is more complex, susceptible to errors, and success cannot be assured. Also, by following these steps you will not get the improvements and bug fixes in the default generated project files.
+
+- [Migrating to go/v4 by updating the files manually][manually-upgrade]
+
+[plugins-phase1-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1.md
+[plugins-phase1-design-doc-1.5]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1-5.md
+[plugins-phase2-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-2.md
+[plugins-section]: ./../plugins/plugins.md
+[kustomize]: https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv4.0.0
+[go/v4-doc]: ./../plugins/go-v4-plugin.md
+[migration-guide-gov3-to-gov4]: migration_guide_gov3_to_gov4.md
+[manually-upgrade]: manually_migration_guide_gov3_to_gov4.md
+[basic-project-doc]: ./../cronjob-tutorial/basic-project.md
+[standard-go-project]: https://github.com/golang-standards/project-layout
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[controller-tools]: https://github.com/kubernetes-sigs/controller-tools
+[kustomize-release]: https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv5.0.0
+[kb-releases]: https://github.com/kubernetes-sigs/kubebuilder/releases
diff --git a/docs/book/src/migrations.md b/docs/book/src/migrations.md
index 3c5fe6c4aa7..cc1fe70b4a8 100644
--- a/docs/book/src/migrations.md
+++ b/docs/book/src/migrations.md
@@ -1,8 +1,78 @@
# Migrations
-Migrating between project structures in KubeBuilder generally involves
-a bit of manual work.
+Upgrading your project scaffold to adopt the latest changes in Kubebuilder may involve migrating to a new plugin
+version (e.g., `go.kubebuilder.io/v3` → `go.kubebuilder.io/v4`)
+or newer CLI toolchain. This process often includes re-scaffolding and
+manually merging your custom code.
-This section details what's required to migrate, between different
-versions of KubeBuilder scaffolding, as well as to more complex project
-layout structures.
+This section details what’s required to migrate, between different versions of Kubebuilder scaffolding,
+as well as to more complex project layout structures.
+
+The manual approach can be error-prone. That is why Kubebuilder introduces new alpha commands
+that help streamline the migration process.
+
+## Manual Migration
+
+The traditional process involves:
+
+- Re-scaffolding the project using the latest Kubebuilder version or plugins
+- Re-adding custom logic manually
+- Running project generators:
+
+ ```bash
+ make generate
+ make manifests
+ ```
+
+## Understanding the PROJECT File (Introduced in `v3.0.0`)
+
+All inputs used by Kubebuilder are tracked in the [PROJECT][project-config] file.
+If you use the CLI to generate your scaffolds, this file will record the project's configuration and metadata.
+
+
+ Project customizations
+
+After using the CLI to create your project, you are free to customise how you see fit.
+Bear in mind, that it is not recommended to deviate from the proposed layout unless you know what you are doing.
+
+For example, you should refrain from moving the scaffolded files, doing so will make it difficult in
+upgrading your project in the future. You may also lose the ability to use some of the CLI
+features and helpers. For further information on the project layout, see
+the doc [What's in a basic project?][basic-project-doc]
+
+
+
+## Alpha Migration Commands
+
+Kubebuilder provides alpha commands to assist with project upgrades.
+
+
+Automation process will involve deleting all files to regenerate
+Deletes all files except `.git` and `PROJECT`.
+
+
+### `kubebuilder alpha generate`
+
+Re-scaffolds the project using the installed CLI version.
+
+```bash
+kubebuilder alpha generate
+```
+
+### `kubebuilder alpha update` (available since `v4.7.0`)
+
+Automates the migration by performing a 3-way merge:
+
+- Original scaffold
+- Your current customized version
+- Latest or specified target scaffold
+
+```bash
+kubebuilder alpha update
+```
+
+For more details, see the [Alpha Command documentation](./reference/alpha_commands.md).
+
+
+[project-config]: ./reference/project-config.md
+[basic-project-doc]: ./cronjob-tutorial/basic-project.md
\ No newline at end of file
diff --git a/docs/book/src/multiversion-tutorial/api-changes.md b/docs/book/src/multiversion-tutorial/api-changes.md
index 57a7495ac36..a5a11c2bcd4 100644
--- a/docs/book/src/multiversion-tutorial/api-changes.md
+++ b/docs/book/src/multiversion-tutorial/api-changes.md
@@ -12,7 +12,7 @@ schedule: "*/1 * * * *"
That's a pretty textbook example of a special string format (it's also
pretty unreadable unless you're a Unix sysadmin).
-Let's make it a bit more structured. According to the our [CronJob
+Let's make it a bit more structured. According to our [CronJob
code][cronjob-sched-code], we support "standard" Cron format.
In Kubernetes, **all versions must be safely round-tripable through each
@@ -40,6 +40,8 @@ We'll need a new API version for this change. Let's call it v2:
kubebuilder create api --group batch --version v2 --kind CronJob
```
+Press `y` for "Create Resource" and `n` for "Create Controller".
+
Now, let's copy over our existing types, and make the change:
{{#literatego ./testdata/project/api/v2/cronjob_types.go}}
@@ -50,4 +52,4 @@ Now, let's copy over our existing types, and make the change:
Now that we've got our types in place, we'll need to set up conversion...
-[cronjob-sched-code]: /TODO.md
+[cronjob-sched-code]: ./multiversion-tutorial/testdata/project/api/v2/cronjob_types.go "CronJob Code"
diff --git a/docs/book/src/multiversion-tutorial/conversion-concepts.md b/docs/book/src/multiversion-tutorial/conversion-concepts.md
index e86748a0a8d..31a324fb54c 100644
--- a/docs/book/src/multiversion-tutorial/conversion-concepts.md
+++ b/docs/book/src/multiversion-tutorial/conversion-concepts.md
@@ -52,7 +52,7 @@ the version stored by the API server.
In that case, the API server needs to know how to convert between the
desired version and the stored version. Since the conversions aren't
built in for CRDs, the Kubernetes API server calls out to a webhook to do
-the conversion instead. For KubeBuilder, this webhook is implemented by
+the conversion instead. For Kubebuilder, this webhook is implemented by
controller-runtime, and performs the hub-and-spoke conversions that we
discussed above.
diff --git a/docs/book/src/multiversion-tutorial/conversion.md b/docs/book/src/multiversion-tutorial/conversion.md
index 443b2a79284..4166992e419 100644
--- a/docs/book/src/multiversion-tutorial/conversion.md
+++ b/docs/book/src/multiversion-tutorial/conversion.md
@@ -1,10 +1,28 @@
# 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.
+
+Conversion Webhooks and Custom Paths
+
+Unlike defaulting and validation webhooks, conversion webhooks do not support custom paths
+via command-line flags. Conversion webhooks use CRD conversion configuration
+(`.spec.conversion.webhook.clientConfig.service.path` in the CRD) rather than webhook
+marker annotations. The path for conversion webhooks is managed differently and cannot
+be customized through kubebuilder flags.
+
+
## Hub...
First, we'll implement the hub. We'll choose the v1 version as the hub:
diff --git a/docs/book/src/multiversion-tutorial/deployment.md b/docs/book/src/multiversion-tutorial/deployment.md
index 64f0b8f46ce..617f0b35dd7 100644
--- a/docs/book/src/multiversion-tutorial/deployment.md
+++ b/docs/book/src/multiversion-tutorial/deployment.md
@@ -1,9 +1,9 @@
# Deployment and Testing
-Before we can test out our conversion, we'll need to enable them conversion in our CRD:
+Before we can test out our conversion, we'll need to enable them in our CRD:
Kubebuilder generates Kubernetes manifests under the `config` directory with webhook
-bits disabled. To enable them, we need to:
+bits disabled. To enable them, we need to:
- Enable `patches/webhook_in_.yaml` and
`patches/cainjection_in_.yaml` in
@@ -12,13 +12,10 @@ bits disabled. To enable them, we need to:
- Enable `../certmanager` and `../webhook` directories under the
`bases` section in `config/default/kustomization.yaml` file.
-- Enable `manager_webhook_patch.yaml` under the `patches` section
- in `config/default/kustomization.yaml` file.
-
- Enable all the vars under the `CERTMANAGER` section in
`config/default/kustomization.yaml` file.
-Additionally, we'll need to set the `CRD_OPTIONS` variable to just
+Additionally, if present in our Makefile, we'll need to set the `CRD_OPTIONS` variable to just
`"crd"`, removing the `trivialVersions` option (this ensures that we
actually [generate validation for each version][ref-multiver], instead of
telling Kubernetes that they're the same):
@@ -34,7 +31,7 @@ You'll need [cert-manager](../cronjob-tutorial/cert-manager.md) installed
(version `0.9.0+`) unless you've got some other certificate management
solution. The Kubebuilder team has tested the instructions in this tutorial
with
-[0.9.0-alpha.0](https://github.com/jetstack/cert-manager/releases/tag/v0.9.0-alpha.0)
+[0.9.0-alpha.0](https://github.com/cert-manager/cert-manager/releases/tag/v0.9.0-alpha.0)
release.
Once all our ducks are in a row with certificates, we can run `make
@@ -43,7 +40,7 @@ controller-manager deployment) onto the cluster.
## Testing
-Once all of the bits are up an running on the cluster with conversion enabled, we can test out our
+Once all of the bits are up and running on the cluster with conversion enabled, we can test out our
conversion by requesting different versions.
We'll make a v2 version based on our v1 version (put it under `config/samples`)
@@ -52,7 +49,7 @@ We'll make a v2 version based on our v1 version (put it under `config/samples`)
{{#include ./testdata/project/config/samples/batch_v2_cronjob.yaml}}
```
-Then, we can create it on the cluster:
+Then, we can create it on the cluster:
```shell
kubectl apply -f config/samples/batch_v2_cronjob.yaml
@@ -114,10 +111,6 @@ resource.version.group` is for everything else.
-## Troubleshooting
-
-[steps for troubleshooting](/TODO.md)
-
[ref-multiver]: /reference/generating-crd.md#multiple-versions "Generating CRDs: Multiple Versions"
[crd-version-pref]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#version-priority "Versions in CustomResourceDefinitions"
diff --git a/docs/book/src/multiversion-tutorial/testdata/generate_multiversion.sh b/docs/book/src/multiversion-tutorial/testdata/generate_multiversion.sh
deleted file mode 100755
index 2c12bf3ba9f..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/generate_multiversion.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright 2020 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
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script is a helper which has just the commands
-# to generate the multiversion tutorial to let us know update manually the testdata dir
-# It allows us run ./generate_mutiversion.sh and git diff with to check what requires updates
-# NOTE: run make install from the project root before execute it.
-
-set -o errexit
-set -o pipefail
-
-# Turn colors in this script off by setting the NO_COLOR variable in your
-# environment to any value:
-#
-# $ NO_COLOR=1 test.sh
-NO_COLOR=${NO_COLOR:-""}
-if [ -z "$NO_COLOR" ]; then
- header=$'\e[1;33m'
- reset=$'\e[0m'
-else
- header=''
- reset=''
-fi
-
-build_kb() {
- go build -o ./bin/kubebuilder sigs.k8s.io/kubebuilder/cmd
-}
-
-function header_text {
- echo "$header$*$reset"
-}
-
-function gen_cronjob_tutorial {
- header_text "removing project ..."
- rm -rf project
- header_text "starting to generate the cronjob ..."
- mkdir project
- cd project
- header_text "generate base ..."
- kubebuilder init --domain=tutorial.kubebuilder.io --project-version=3 --repo=tutorial.kubebuilder.io/project --license apache2 --owner "The Kubernetes authors"
- kubebuilder create api --group batch --version v1 --kind CronJob --resource --controller --make=false
- kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation
- kubebuilder create webhook --group batch --version v1 --kind CronJob --conversion --force
- kubebuilder create api --group batch --version v2 --kind CronJob --resource=true --controller=false
- kubebuilder create webhook --group batch --version v2 --kind CronJob --defaulting --programmatic-validation
- kubebuilder create webhook --group batch --version v2 --kind CronJob --conversion --force
-}
-
-
-gen_cronjob_tutorial
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json b/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json
new file mode 100644
index 00000000000..a3ab7541cb6
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/devcontainer.json
@@ -0,0 +1,25 @@
+{
+ "name": "Kubebuilder DevContainer",
+ "image": "golang:1.24",
+ "features": {
+ "ghcr.io/devcontainers/features/docker-in-docker:2": {},
+ "ghcr.io/devcontainers/features/git:1": {}
+ },
+
+ "runArgs": ["--network=host"],
+
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.shell.linux": "/bin/bash"
+ },
+ "extensions": [
+ "ms-kubernetes-tools.vscode-kubernetes-tools",
+ "ms-azuretools.vscode-docker"
+ ]
+ }
+ },
+
+ "onCreateCommand": "bash .devcontainer/post-install.sh"
+}
+
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/post-install.sh b/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/post-install.sh
new file mode 100644
index 00000000000..67f3e97ab32
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.devcontainer/post-install.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -x
+
+curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-$(go env GOARCH)
+chmod +x ./kind
+mv ./kind /usr/local/bin/kind
+
+curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/$(go env GOARCH)
+chmod +x kubebuilder
+mv kubebuilder /usr/local/bin/
+
+KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
+curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/$(go env GOARCH)/kubectl"
+chmod +x kubectl
+mv kubectl /usr/local/bin/kubectl
+
+docker network create -d=bridge --subnet=172.19.0.0/24 kind
+
+kind version
+kubebuilder version
+docker --version
+go version
+kubectl version --client
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.dockerignore b/docs/book/src/multiversion-tutorial/testdata/project/.dockerignore
index 243f81a5080..9af8280793c 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/.dockerignore
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.dockerignore
@@ -1,5 +1,11 @@
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
-# Ignore all files which are not go type
+# Ignore everything by default and re-include only needed files
+**
+
+# Re-include Go source files (but not *_test.go)
!**/*.go
-!**/*.mod
-!**/*.sum
+**/*_test.go
+
+# Re-include Go module files
+!go.mod
+!go.sum
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
new file mode 100644
index 00000000000..029218b7232
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/lint.yml
@@ -0,0 +1,23 @@
+name: Lint
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ lint:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+
+ - name: Run linter
+ uses: golangci/golangci-lint-action@v8
+ with:
+ version: v2.6.0
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-chart.yml b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-chart.yml
new file mode 100644
index 00000000000..cc3070bd7a0
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-chart.yml
@@ -0,0 +1,74 @@
+name: Test Chart
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test-e2e:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ 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-$(go env GOARCH)
+ 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
+ run: |
+ go mod tidy
+ make docker-build IMG=project:v0.1.0
+ kind load docker-image project: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
+ run: |
+ helm lint ./dist/chart
+
+
+ - name: Install cert-manager via Helm (wait for readiness)
+ 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 crds.enabled=true \
+ --wait \
+ --timeout 300s
+
+# TODO: Uncomment if Prometheus is enabled
+# - name: Install Prometheus Operator CRDs
+# run: |
+# helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
+# helm repo update
+# helm install prometheus-crds prometheus-community/prometheus-operator-crds
+
+ - name: Install Helm chart for project
+ run: |
+ helm install my-release ./dist/chart --create-namespace --namespace project-system
+
+ - name: Check Helm release status
+ run: |
+ helm status my-release --namespace project-system
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
new file mode 100644
index 00000000000..4cdfb30e026
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test-e2e.yml
@@ -0,0 +1,32 @@
+name: E2E Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test-e2e:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ 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-$(go env GOARCH)
+ chmod +x ./kind
+ sudo mv ./kind /usr/local/bin/kind
+
+ - name: Verify kind installation
+ run: kind version
+
+ - name: Running Test e2e
+ run: |
+ go mod tidy
+ make test-e2e
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
new file mode 100644
index 00000000000..fc2e80d304d
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.github/workflows/test.yml
@@ -0,0 +1,23 @@
+name: Tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ test:
+ name: Run on Ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone the code
+ uses: actions/checkout@v4
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version-file: go.mod
+
+ - name: Running Tests
+ run: |
+ go mod tidy
+ make test
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.gitignore b/docs/book/src/multiversion-tutorial/testdata/project/.gitignore
index c0a7a54cac5..9f0f3a1c673 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/.gitignore
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.gitignore
@@ -1,25 +1,30 @@
-
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
-bin
-testbin/*
+bin/*
+Dockerfile.cross
-# Test binary, build with `go test -c`
+# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-# Kubernetes Generated files - skip generated files, except for vendored files
+# Go workspace file
+go.work
+# Kubernetes Generated files - skip generated files, except for vendored files
!vendor/**/zz_generated.*
# editor and IDE paraphernalia
.idea
+.vscode
*.swp
*.swo
*~
+
+# Kubeconfig might contain secrets
+*.kubeconfig
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/.golangci.yml b/docs/book/src/multiversion-tutorial/testdata/project/.golangci.yml
new file mode 100644
index 00000000000..e5b21b0f11c
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/.golangci.yml
@@ -0,0 +1,52 @@
+version: "2"
+run:
+ allow-parallel-runners: true
+linters:
+ default: none
+ enable:
+ - copyloopvar
+ - dupl
+ - errcheck
+ - ginkgolinter
+ - goconst
+ - gocyclo
+ - govet
+ - ineffassign
+ - lll
+ - misspell
+ - nakedret
+ - prealloc
+ - revive
+ - staticcheck
+ - unconvert
+ - unparam
+ - unused
+ settings:
+ revive:
+ rules:
+ - name: comment-spacings
+ - name: import-shadowing
+ exclusions:
+ generated: lax
+ rules:
+ - linters:
+ - lll
+ path: api/*
+ - linters:
+ - dupl
+ - lll
+ path: internal/*
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
+formatters:
+ enable:
+ - gofmt
+ - goimports
+ exclusions:
+ generated: lax
+ paths:
+ - third_party$
+ - builtin$
+ - examples$
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile b/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile
index ce816f3b0e7..6466c484903 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile
+++ b/docs/book/src/multiversion-tutorial/testdata/project/Dockerfile
@@ -1,5 +1,7 @@
# Build the manager binary
-FROM golang:1.15 as builder
+FROM golang:1.24 AS builder
+ARG TARGETOS
+ARG TARGETARCH
WORKDIR /workspace
# Copy the Go Modules manifests
@@ -9,13 +11,15 @@ COPY go.sum go.sum
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
-# Copy the go source
-COPY main.go main.go
-COPY api/ api/
-COPY controllers/ controllers/
+# Copy the Go source (relies on .dockerignore to filter)
+COPY . .
# Build
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
+# the GOARCH has no default value to allow the binary to be built according to the host where the command
+# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
+# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
+# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/Makefile b/docs/book/src/multiversion-tutorial/testdata/project/Makefile
index 79c2a4b645b..838c45f3ec1 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/Makefile
+++ b/docs/book/src/multiversion-tutorial/testdata/project/Makefile
@@ -1,8 +1,5 @@
-
# Image URL to use all building/pushing image targets
IMG ?= controller:latest
-# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
-CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
@@ -11,19 +8,25 @@ else
GOBIN=$(shell go env GOBIN)
endif
+# CONTAINER_TOOL defines the container tool to be used for building images.
+# Be aware that the target commands are only tested with Docker which is
+# scaffolded by default. However, you might want to replace it to use other
+# tools. (i.e. podman)
+CONTAINER_TOOL ?= docker
+
# Setting SHELL to bash allows bash commands to be executed by recipes.
-# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
+.PHONY: all
all: build
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
-# target descriptions by '##'. The awk commands is responsible for reading the
+# target descriptions by '##'. The awk command is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
@@ -32,77 +35,220 @@ all: build
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
+.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Development
+.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
- $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+ # Note that the option maxDescLen=0 was added in the default scaffold in order to sort out the issue
+ # Too long: must have at most 262144 bytes. By using kubectl apply to create / update resources an annotation
+ # is created by K8s API to store the latest version of the resource ( kubectl.kubernetes.io/last-applied-configuration).
+ # However, it has a size limit and if the CRD is too big with so many long descriptions as this one it will cause the failure.
+ "$(CONTROLLER_GEN)" rbac:roleName=manager-role crd:maxDescLen=0 webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
- $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
+ "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt" paths="./..."
+.PHONY: fmt
fmt: ## Run go fmt against code.
go fmt ./...
+.PHONY: vet
vet: ## Run go vet against code.
go vet ./...
-ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
-test: manifests generate fmt vet ## Run tests.
- mkdir -p ${ENVTEST_ASSETS_DIR}
- test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.2/hack/setup-envtest.sh
- source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out
+.PHONY: test
+test: manifests generate fmt vet setup-envtest ## Run tests.
+ KUBEBUILDER_ASSETS="$(shell "$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
+
+# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
+# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
+# CertManager is installed by default; skip with:
+# - CERT_MANAGER_INSTALL_SKIP=true
+KIND_CLUSTER ?= project-test-e2e
+
+.PHONY: setup-test-e2e
+setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
+ @command -v $(KIND) >/dev/null 2>&1 || { \
+ echo "Kind is not installed. Please install Kind manually."; \
+ exit 1; \
+ }
+ @case "$$($(KIND) get clusters)" in \
+ *"$(KIND_CLUSTER)"*) \
+ echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
+ *) \
+ echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
+ $(KIND) create cluster --name $(KIND_CLUSTER) ;; \
+ esac
+
+.PHONY: test-e2e
+test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
+ KIND=$(KIND) KIND_CLUSTER=$(KIND_CLUSTER) go test -tags=e2e ./test/e2e/ -v -ginkgo.v
+ $(MAKE) cleanup-test-e2e
+
+.PHONY: cleanup-test-e2e
+cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
+ @$(KIND) delete cluster --name $(KIND_CLUSTER)
+
+.PHONY: lint
+lint: golangci-lint ## Run golangci-lint linter
+ "$(GOLANGCI_LINT)" run
+
+.PHONY: lint-fix
+lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
+ "$(GOLANGCI_LINT)" run --fix
+
+.PHONY: lint-config
+lint-config: golangci-lint ## Verify golangci-lint linter configuration
+ "$(GOLANGCI_LINT)" config verify
##@ Build
-build: generate fmt vet ## Build manager binary.
- go build -o bin/manager main.go
+.PHONY: build
+build: manifests generate fmt vet ## Build manager binary.
+ go build -o bin/manager cmd/main.go
+.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
- go run ./main.go
+ go run ./cmd/main.go
-docker-build: test ## Build docker image with the manager.
- docker build -t ${IMG} .
+# If you wish to build the manager image targeting other platforms you can use the --platform flag.
+# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
+# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+.PHONY: docker-build
+docker-build: ## Build docker image with the manager.
+ $(CONTAINER_TOOL) build -t ${IMG} .
+.PHONY: docker-push
docker-push: ## Push docker image with the manager.
- docker push ${IMG}
+ $(CONTAINER_TOOL) push ${IMG}
+
+# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
+# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
+# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
+# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail)
+# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
+PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
+.PHONY: docker-buildx
+docker-buildx: ## Build and push docker image for the manager for cross-platform support
+ # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
+ sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
+ - $(CONTAINER_TOOL) buildx create --name project-builder
+ $(CONTAINER_TOOL) buildx use project-builder
+ - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
+ - $(CONTAINER_TOOL) buildx rm project-builder
+ rm Dockerfile.cross
+
+.PHONY: build-installer
+build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
+ mkdir -p dist
+ cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG}
+ "$(KUSTOMIZE)" build config/default > dist/install.yaml
##@ Deployment
+ifndef ignore-not-found
+ ignore-not-found = false
+endif
+
+.PHONY: install
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/crd | kubectl apply -f -
+ @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
+ if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" apply -f -; else echo "No CRDs to install; skipping."; fi
-uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/crd | kubectl delete -f -
+.PHONY: uninstall
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+ @out="$$( "$(KUSTOMIZE)" build config/crd 2>/dev/null || true )"; \
+ if [ -n "$$out" ]; then echo "$$out" | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -; else echo "No CRDs to delete; skipping."; fi
+.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
- cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
- $(KUSTOMIZE) build config/default | kubectl apply -f -
-
-undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
- $(KUSTOMIZE) build config/default | kubectl delete -f -
-
-
-CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
-controller-gen: ## Download controller-gen locally if necessary.
- $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
-
-KUSTOMIZE = $(shell pwd)/bin/kustomize
-kustomize: ## Download kustomize locally if necessary.
- $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
-
-# go-get-tool will 'go get' any package $2 and install it to $1.
-PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
-define go-get-tool
-@[ -f $(1) ] || { \
-set -e ;\
-TMP_DIR=$$(mktemp -d) ;\
-cd $$TMP_DIR ;\
-go mod init tmp ;\
-echo "Downloading $(2)" ;\
-GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
-rm -rf $$TMP_DIR ;\
-}
+ cd config/manager && "$(KUSTOMIZE)" edit set image controller=${IMG}
+ "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" apply -f -
+
+.PHONY: undeploy
+undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+ "$(KUSTOMIZE)" build config/default | "$(KUBECTL)" delete --ignore-not-found=$(ignore-not-found) -f -
+
+##@ Dependencies
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+ mkdir -p "$(LOCALBIN)"
+
+## Tool Binaries
+KUBECTL ?= kubectl
+KIND ?= kind
+KUSTOMIZE ?= $(LOCALBIN)/kustomize
+CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
+
+## Tool Versions
+KUSTOMIZE_VERSION ?= v5.7.1
+CONTROLLER_TOOLS_VERSION ?= v0.19.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 v='$(call gomodver,sigs.k8s.io/controller-runtime)'; \
+ [ -n "$$v" ] || { echo "Set ENVTEST_VERSION manually (controller-runtime replace has no tag)" >&2; exit 1; }; \
+ printf '%s\n' "$$v" | sed -E 's/^v?([0-9]+)\.([0-9]+).*/release-\1.\2/')
+
+#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
+ENVTEST_K8S_VERSION ?= $(shell v='$(call gomodver,k8s.io/api)'; \
+ [ -n "$$v" ] || { echo "Set ENVTEST_K8S_VERSION manually (k8s.io/api replace has no tag)" >&2; exit 1; }; \
+ printf '%s\n' "$$v" | sed -E 's/^v?[0-9]+\.([0-9]+).*/1.\1/')
+
+GOLANGCI_LINT_VERSION ?= v2.6.0
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
+$(KUSTOMIZE): $(LOCALBIN)
+ $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
+
+.PHONY: controller-gen
+controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
+$(CONTROLLER_GEN): $(LOCALBIN)
+ $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
+
+.PHONY: setup-envtest
+setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
+ @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..."
+ @"$(ENVTEST)" use $(ENVTEST_K8S_VERSION) --bin-dir "$(LOCALBIN)" -p path || { \
+ echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \
+ exit 1; \
+ }
+
+.PHONY: envtest
+envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
+$(ENVTEST): $(LOCALBIN)
+ $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
+
+.PHONY: golangci-lint
+golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
+$(GOLANGCI_LINT): $(LOCALBIN)
+ $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
+
+# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
+# $1 - target path with name of binary
+# $2 - package url which can be installed
+# $3 - specific version of package
+define go-install-tool
+@[ -f "$(1)-$(3)" ] && [ "$$(readlink -- "$(1)" 2>/dev/null)" = "$(1)-$(3)" ] || { \
+set -e; \
+package=$(2)@$(3) ;\
+echo "Downloading $${package}" ;\
+rm -f "$(1)" ;\
+GOBIN="$(LOCALBIN)" go install $${package} ;\
+mv "$(LOCALBIN)/$$(basename "$(1)")" "$(1)-$(3)" ;\
+} ;\
+ln -sf "$$(realpath "$(1)-$(3)")" "$(1)"
+endef
+
+define gomodver
+$(shell go list -m -f '{{if .Replace}}{{.Replace.Version}}{{else}}{{.Version}}{{end}}' $(1) 2>/dev/null)
endef
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/PROJECT b/docs/book/src/multiversion-tutorial/testdata/project/PROJECT
index e722a24fad9..2fa0948c203 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/PROJECT
+++ b/docs/book/src/multiversion-tutorial/testdata/project/PROJECT
@@ -1,6 +1,15 @@
+# Code generated by tool. DO NOT EDIT.
+# This file is used to track the info used to scaffold your project
+# and allow the plugins properly work.
+# More info: https://book.kubebuilder.io/reference/project-config.html
+cliVersion: (devel)
domain: tutorial.kubebuilder.io
layout:
-- go.kubebuilder.io/v3
+- go.kubebuilder.io/v4
+plugins:
+ helm.kubebuilder.io/v2-alpha:
+ manifests: dist/install.yaml
+ output: dist
projectName: project
repo: tutorial.kubebuilder.io/project
resources:
@@ -16,6 +25,8 @@ resources:
webhooks:
conversion: true
defaulting: true
+ spoke:
+ - v2
validation: true
webhookVersion: v1
- api:
@@ -27,7 +38,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
new file mode 100644
index 00000000000..fcc53b94123
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/README.md
@@ -0,0 +1,135 @@
+# project
+// TODO(user): Add simple overview of use/purpose
+
+## Description
+// TODO(user): An in-depth paragraph about your project and overview of use
+
+## Getting Started
+
+### Prerequisites
+- go version v1.24.6+
+- docker version 17.03+.
+- kubectl version v1.11.3+.
+- Access to a Kubernetes v1.11.3+ cluster.
+
+### To Deploy on the cluster
+**Build and push your image to the location specified by `IMG`:**
+
+```sh
+make docker-build docker-push IMG=/project:tag
+```
+
+**NOTE:** This image ought to be published in the personal registry you specified.
+And it is required to have access to pull the image from the working environment.
+Make sure you have the proper permission to the registry if the above commands don’t work.
+
+**Install the CRDs into the cluster:**
+
+```sh
+make install
+```
+
+**Deploy the Manager to the cluster with the image specified by `IMG`:**
+
+```sh
+make deploy IMG=/project:tag
+```
+
+> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin
+privileges or be logged in as admin.
+
+**Create instances of your solution**
+You can apply the samples (examples) from the config/sample:
+
+```sh
+kubectl apply -k config/samples/
+```
+
+>**NOTE**: Ensure that the samples has default values to test it out.
+
+### To Uninstall
+**Delete the instances (CRs) from the cluster:**
+
+```sh
+kubectl delete -k config/samples/
+```
+
+**Delete the APIs(CRDs) from the cluster:**
+
+```sh
+make uninstall
+```
+
+**UnDeploy the controller from the cluster:**
+
+```sh
+make undeploy
+```
+
+## Project Distribution
+
+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:
+
+```sh
+make build-installer IMG=/project:tag
+```
+
+**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.
+
+2. Using the installer
+
+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/v2-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
+
+**NOTE:** Run `make help` for more information on all potential `make` targets
+
+More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
+
+## License
+
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under 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 ced8df86aa0..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,5 @@
/*
+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 +19,9 @@ package v1
/*
Implementing the hub method is pretty easy -- we just have to add an empty
-method called `Hub()` to serve as a
+method called `Hub()`to serve as a
[marker](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Hub).
-We could also just put this inline in our `cronjob_types.go` file.
+We could also just put this inline in our cronjob_types.go file.
*/
// Hub marks this type as a conversion hub.
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 04c04d15620..9e3412ff0eb 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 2021 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.
@@ -17,12 +17,14 @@ limitations under the License.
/*
*/
+
package v1
/*
*/
+
import (
- batchv1beta1 "k8s.io/api/batch/v1beta1"
+ batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -34,46 +36,45 @@ import (
// CronJobSpec defines the desired state of CronJob
type CronJobSpec struct {
+ // schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
// +kubebuilder:validation:MinLength=0
-
- // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
+ // +required
Schedule string `json:"schedule"`
- // +kubebuilder:validation:Minimum=0
-
- // Optional deadline in seconds for starting the job if it misses scheduled
+ // startingDeadlineSeconds defines in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones.
// +optional
+ // +kubebuilder:validation:Minimum=0
StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
- // Specifies how to treat concurrent executions of a Job.
+ // concurrencyPolicy specifies how to treat concurrent executions of a Job.
// Valid values are:
// - "Allow" (default): allows CronJobs to run concurrently;
// - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet;
// - "Replace": cancels currently running job and replaces it with a new one
// +optional
+ // +kubebuilder:default:=Allow
ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
- // This flag tells the controller to suspend subsequent executions, it does
+ // suspend tells the controller to suspend subsequent executions, it does
// not apply to already started executions. Defaults to false.
// +optional
Suspend *bool `json:"suspend,omitempty"`
- // Specifies the job that will be created when executing a CronJob.
- JobTemplate batchv1beta1.JobTemplateSpec `json:"jobTemplate"`
-
- // +kubebuilder:validation:Minimum=0
+ // jobTemplate defines the job that will be created when executing a CronJob.
+ // +required
+ JobTemplate batchv1.JobTemplateSpec `json:"jobTemplate"`
- // The number of successful finished jobs to retain.
+ // successfulJobsHistoryLimit defines the number of successful finished jobs to retain.
// This is a pointer to distinguish between explicit zero and not specified.
// +optional
- SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
-
// +kubebuilder:validation:Minimum=0
+ SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
- // The number of failed finished jobs to retain.
+ // failedJobsHistoryLimit defines the number of failed finished jobs to retain.
// This is a pointer to distinguish between explicit zero and not specified.
// +optional
+ // +kubebuilder:validation:Minimum=0
FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"`
}
@@ -96,56 +97,85 @@ const (
ReplaceConcurrent ConcurrencyPolicy = "Replace"
)
-// CronJobStatus defines the observed state of CronJob
+// CronJobStatus defines the observed state of CronJob.
type CronJobStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
- // A list of pointers to currently running jobs.
+ // active defines a list of pointers to currently running jobs.
// +optional
+ // +listType=atomic
+ // +kubebuilder:validation:MinItems=1
+ // +kubebuilder:validation:MaxItems=10
Active []corev1.ObjectReference `json:"active,omitempty"`
- // Information when was the last time the job was successfully scheduled.
+ // lastScheduleTime defines when was the last time the job was successfully scheduled.
// +optional
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
+
+ // For Kubernetes API conventions, see:
+ // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
+
+ // conditions represent the current state of the CronJob resource.
+ // Each condition has a unique type and reflects the status of a specific aspect of the resource.
+ //
+ // Standard condition types include:
+ // - "Available": the resource is fully functional
+ // - "Progressing": the resource is being created or updated
+ // - "Degraded": the resource failed to reach or maintain its desired state
+ //
+ // The status of each condition is one of True, False, or Unknown.
+ // +listType=map
+ // +listMapKey=type
+ // +optional
+ Conditions []metav1.Condition `json:"conditions,omitempty"`
}
-// +kubebuilder:docs-gen:collapse=old stuff
+// +kubebuilder:docs-gen:collapse=Remaining code from cronjob_types.go
/*
-Since we'll have more than one version, we'll need to mark a storage version.
-This is the version that the Kubernetes API server uses to store our data.
-We'll chose the v1 version for our project.
+ Since we'll have more than one version, we'll need to mark a storage version.
+ This is the version that the Kubernetes API server uses to store our data.
+ We'll chose the v1 version for our project.
-We'll use the [`+kubebuilder:storageversion`](/reference/markers/crd.md) to do this.
+ We'll use the [`+kubebuilder:storageversion`](/reference/markers/crd.md) to do this.
-Note that multiple versions may exist in storage if they were written before
-the storage version changes -- changing the storage version only affects how
-objects are created/updated after the change.
+ Note that multiple versions may exist in storage if they were written before
+ the storage version changes -- changing the storage version only affects how
+ objects are created/updated after the change.
*/
-//+kubebuilder:object:root=true
-//+kubebuilder:subresource:status
-//+kubebuilder:storageversion
-
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +versionName=v1
+// +kubebuilder:storageversion
// CronJob is the Schema for the cronjobs API
type CronJob struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
+ metav1.TypeMeta `json:",inline"`
+
+ // metadata is a standard object metadata
+ // +optional
+ metav1.ObjectMeta `json:"metadata,omitzero"`
- Spec CronJobSpec `json:"spec,omitempty"`
- Status CronJobStatus `json:"status,omitempty"`
+ // spec defines the desired state of CronJob
+ // +required
+ Spec CronJobSpec `json:"spec"`
+
+ // status defines the observed state of CronJob
+ // +optional
+ Status CronJobStatus `json:"status,omitzero"`
}
/*
*/
-//+kubebuilder:object:root=true
+// +kubebuilder:object:root=true
// CronJobList contains a list of CronJob
type CronJobList struct {
metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata,omitempty"`
+ metav1.ListMeta `json:"metadata,omitzero"`
Items []CronJob `json:"items"`
}
@@ -153,4 +183,4 @@ func init() {
SchemeBuilder.Register(&CronJob{}, &CronJobList{})
}
-// +kubebuilder:docs-gen:collapse=old stuff
+// +kubebuilder:docs-gen:collapse=Remaining code from cronjob_types.go
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_webhook.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_webhook.go
deleted file mode 100644
index dfdfc7bae8a..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/cronjob_webhook.go
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-package v1
-
-import (
- "github.com/robfig/cron"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/runtime/schema"
- validationutils "k8s.io/apimachinery/pkg/util/validation"
- "k8s.io/apimachinery/pkg/util/validation/field"
- ctrl "sigs.k8s.io/controller-runtime"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/webhook"
-)
-
-//+kubebuilder:docs-gen:collapse=Go imports
-
-var cronjoblog = logf.Log.WithName("cronjob-resource")
-
-/*
-This setup doubles as setup for our conversion webhooks: as long as our
-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.
-*/
-
-func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(r).
- Complete()
-}
-
-/*
- */
-
-// +kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
-
-var _ webhook.Defaulter = &CronJob{}
-
-// Default implements webhook.Defaulter so a webhook will be registered for the type
-func (r *CronJob) Default() {
- cronjoblog.Info("default", "name", r.Name)
-
- if r.Spec.ConcurrencyPolicy == "" {
- r.Spec.ConcurrencyPolicy = AllowConcurrent
- }
- if r.Spec.Suspend == nil {
- r.Spec.Suspend = new(bool)
- }
- if r.Spec.SuccessfulJobsHistoryLimit == nil {
- r.Spec.SuccessfulJobsHistoryLimit = new(int32)
- *r.Spec.SuccessfulJobsHistoryLimit = 3
- }
- if r.Spec.FailedJobsHistoryLimit == nil {
- r.Spec.FailedJobsHistoryLimit = new(int32)
- *r.Spec.FailedJobsHistoryLimit = 1
- }
-}
-
-// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,versions=v1,name=vcronjob.kb.io,sideEffects=None,admissionReviewVersions=v1
-
-var _ webhook.Validator = &CronJob{}
-
-// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateCreate() error {
- cronjoblog.Info("validate create", "name", r.Name)
-
- return r.validateCronJob()
-}
-
-// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateUpdate(old runtime.Object) error {
- cronjoblog.Info("validate update", "name", r.Name)
-
- return r.validateCronJob()
-}
-
-// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
-func (r *CronJob) ValidateDelete() error {
- cronjoblog.Info("validate delete", "name", r.Name)
-
- // TODO(user): fill in your validation logic upon object deletion.
- return nil
-}
-
-func (r *CronJob) validateCronJob() error {
- var allErrs field.ErrorList
- if err := r.validateCronJobName(); err != nil {
- allErrs = append(allErrs, err)
- }
- if err := r.validateCronJobSpec(); err != nil {
- allErrs = append(allErrs, err)
- }
- if len(allErrs) == 0 {
- return nil
- }
-
- return apierrors.NewInvalid(
- schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"},
- r.Name, allErrs)
-}
-
-func (r *CronJob) validateCronJobSpec() *field.Error {
- // The field helpers from the kubernetes API machinery help us return nicely
- // structured validation errors.
- return validateScheduleFormat(
- r.Spec.Schedule,
- field.NewPath("spec").Child("schedule"))
-}
-
-func validateScheduleFormat(schedule string, fldPath *field.Path) *field.Error {
- if _, err := cron.ParseStandard(schedule); err != nil {
- return field.Invalid(fldPath, schedule, err.Error())
- }
- return nil
-}
-
-func (r *CronJob) validateCronJobName() *field.Error {
- if len(r.ObjectMeta.Name) > validationutils.DNS1035LabelMaxLength-11 {
- // The job name length is 63 character like all Kubernetes objects
- // (which must fit in a DNS subdomain). The cronjob controller appends
- // a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
- // a job. The job name length limit is 63 characters. Therefore cronjob
- // names must have length <= 63-11=52. If we don't validate this here,
- // then job creation will fail later.
- return field.Invalid(field.NewPath("metadata").Child("name"), r.Name, "must be no more than 52 characters")
- }
- return nil
-}
-
-// +kubebuilder:docs-gen:collapse=Existing Defaulting and Validation
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 57645bc1725..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 2021 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.
@@ -23,9 +23,9 @@ former, while the latter is used by the CRD generator to generate the right
metadata for the CRDs it creates from this package.
*/
-// Package v1 contains API Schema definitions for the batch v1 API group
-//+kubebuilder:object:generate=true
-//+groupName=batch.tutorial.kubebuilder.io
+// Package v1 contains API Schema definitions for the batch v1 API group.
+// +kubebuilder:object:generate=true
+// +groupName=batch.tutorial.kubebuilder.io
package v1
import (
@@ -39,11 +39,12 @@ Since we need to use all the types in this package in our controller, it's
helpful (and the convention) to have a convenient method to add all the types to
some other `Scheme`. SchemeBuilder makes this easy for us.
*/
+
var (
- // GroupVersion is group version used to register these objects
+ // GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v1"}
- // SchemeBuilder is used to add go types to the GroupVersionKind scheme
+ // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/webhook_suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v1/webhook_suite_test.go
deleted file mode 100644
index 93992eb037c..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/api/v1/webhook_suite_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v1
-
-import (
- "context"
- "crypto/tls"
- "fmt"
- "net"
- "path/filepath"
- "testing"
- "time"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
-
- admissionv1beta1 "k8s.io/api/admission/v1beta1"
- //+kubebuilder:scaffold:imports
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/rest"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-)
-
-// 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
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Webhook Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- ctx, cancel = context.WithCancel(context.TODO())
-
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
- ErrorIfCRDPathMissing: false,
- WebhookInstallOptions: envtest.WebhookInstallOptions{
- Paths: []string{filepath.Join("..", "..", "config", "webhook")},
- },
- }
-
- cfg, err := testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
-
- scheme := runtime.NewScheme()
- err = AddToScheme(scheme)
- Expect(err).NotTo(HaveOccurred())
-
- err = admissionv1beta1.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())
-
- // start webhook server using Manager
- webhookInstallOptions := &testEnv.WebhookInstallOptions
- mgr, err := ctrl.NewManager(cfg, ctrl.Options{
- Scheme: scheme,
- Host: webhookInstallOptions.LocalServingHost,
- Port: webhookInstallOptions.LocalServingPort,
- CertDir: webhookInstallOptions.LocalServingCertDir,
- LeaderElection: false,
- MetricsBindAddress: "0",
- })
- Expect(err).NotTo(HaveOccurred())
-
- err = (&CronJob{}).SetupWebhookWithManager(mgr)
- Expect(err).NotTo(HaveOccurred())
-
- //+kubebuilder:scaffold:webhook
-
- go func() {
- err = mgr.Start(ctx)
- if err != nil {
- Expect(err).NotTo(HaveOccurred())
- }
- }()
-
- // wait for the webhook server to get ready
- dialer := &net.Dialer{Timeout: time.Second}
- addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
- Eventually(func() error {
- conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
- if err != nil {
- return err
- }
- conn.Close()
- return nil
- }).Should(Succeed())
-
-}, 60)
-
-var _ = AfterSuite(func() {
- cancel()
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
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 844bf6fd983..19c26d31490 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 @@
-// +build !ignore_autogenerated
+//go:build !ignore_autogenerated
/*
-Copyright 2021 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,7 +22,8 @@ package v1
import (
corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/runtime"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@@ -132,6 +133,13 @@ func (in *CronJobStatus) DeepCopyInto(out *CronJobStatus) {
in, out := &in.LastScheduleTime, &out.LastScheduleTime
*out = (*in).DeepCopy()
}
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]metav1.Condition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobStatus.
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 6b713e8f520..1d0d146227e 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,31 +23,35 @@ 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"
- "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
[`Convertible`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/conversion?tab=doc#Convertible)
-interface. Namely, they'll need `ConvertTo` and `ConvertFrom` methods to convert to/from
-the hub version.
+interface. Namely, they'll need `ConvertTo()` and `ConvertFrom()`
+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
@@ -84,18 +90,21 @@ func (src *CronJob) ConvertTo(dstRaw conversion.Hub) error {
dst.Status.Active = src.Status.Active
dst.Status.LastScheduleTime = src.Status.LastScheduleTime
- // +kubebuilder:docs-gen:collapse=rote conversion
return nil
}
+// +kubebuilder:docs-gen:collapse=rote conversion
+
/*
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 {
@@ -132,6 +141,7 @@ func (dst *CronJob) ConvertFrom(srcRaw conversion.Hub) error {
dst.Status.Active = src.Status.Active
dst.Status.LastScheduleTime = src.Status.LastScheduleTime
- // +kubebuilder:docs-gen:collapse=rote conversion
return nil
}
+
+// +kubebuilder:docs-gen:collapse=rote conversion
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 274a36b769d..78c58ae3b8f 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 2021 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,12 +20,13 @@ Since we're in a v2 package, controller-gen will assume this is for the v2
version automatically. We could override that with the [`+versionName`
marker](/reference/markers/crd.md).
*/
+
package v2
/*
*/
import (
- batchv1beta1 "k8s.io/api/batch/v1beta1"
+ batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -40,52 +41,52 @@ We'll leave our spec largely unchanged, except to change the schedule field to a
*/
// CronJobSpec defines the desired state of CronJob
type CronJobSpec struct {
- // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
+ // schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
+ // +required
Schedule CronSchedule `json:"schedule"`
/*
*/
- // +kubebuilder:validation:Minimum=0
-
- // Optional deadline in seconds for starting the job if it misses scheduled
+ // startingDeadlineSeconds defines in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones.
// +optional
+ // +kubebuilder:validation:Minimum=0
StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
- // Specifies how to treat concurrent executions of a Job.
+ // concurrencyPolicy defines how to treat concurrent executions of a Job.
// Valid values are:
// - "Allow" (default): allows CronJobs to run concurrently;
// - "Forbid": forbids concurrent runs, skipping next run if previous run hasn't finished yet;
// - "Replace": cancels currently running job and replaces it with a new one
// +optional
+ // +kubebuilder:default:=Allow
ConcurrencyPolicy ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
- // This flag tells the controller to suspend subsequent executions, it does
+ // suspend tells the controller to suspend subsequent executions, it does
// not apply to already started executions. Defaults to false.
// +optional
Suspend *bool `json:"suspend,omitempty"`
- // Specifies the job that will be created when executing a CronJob.
- JobTemplate batchv1beta1.JobTemplateSpec `json:"jobTemplate"`
-
- // +kubebuilder:validation:Minimum=0
+ // jobTemplate defines the job that will be created when executing a CronJob.
+ // +required
+ JobTemplate batchv1.JobTemplateSpec `json:"jobTemplate"`
- // The number of successful finished jobs to retain.
+ // successfulJobsHistoryLimit defines the number of successful finished jobs to retain.
// This is a pointer to distinguish between explicit zero and not specified.
// +optional
- SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
-
// +kubebuilder:validation:Minimum=0
+ SuccessfulJobsHistoryLimit *int32 `json:"successfulJobsHistoryLimit,omitempty"`
- // The number of failed finished jobs to retain.
+ // failedJobsHistoryLimit defines the number of failed finished jobs to retain.
// This is a pointer to distinguish between explicit zero and not specified.
// +optional
+ // +kubebuilder:validation:Minimum=0
FailedJobsHistoryLimit *int32 `json:"failedJobsHistoryLimit,omitempty"`
-
- //+kubebuilder:docs-gen:collapse=The rest of Spec
}
+// +kubebuilder:docs-gen:collapse=CronJobSpec Full Code
+
/*
Next, we'll need to define a type to hold our schedule.
Based on our proposed YAML above, it'll have a field for
@@ -94,19 +95,19 @@ each corresponding Cron "field".
// describes a Cron schedule.
type CronSchedule struct {
- // specifies the minute during which the job executes.
+ // minute specifies the minutes during which the job executes.
// +optional
Minute *CronField `json:"minute,omitempty"`
- // specifies the hour during which the job executes.
+ // hour specifies the hour during which the job executes.
// +optional
Hour *CronField `json:"hour,omitempty"`
- // specifies the day of the month during which the job executes.
+ // dayOfMonth specifies the day of the month during which the job executes.
// +optional
DayOfMonth *CronField `json:"dayOfMonth,omitempty"`
- // specifies the month during which the job executes.
+ // month specifies the month during which the job executes.
// +optional
Month *CronField `json:"month,omitempty"`
- // specifies the day of the week during which the job executes.
+ // dayOfWeek specifies the day of the week during which the job executes.
// +optional
DayOfWeek *CronField `json:"dayOfWeek,omitempty"`
}
@@ -143,38 +144,65 @@ const (
ReplaceConcurrent ConcurrencyPolicy = "Replace"
)
-// CronJobStatus defines the observed state of CronJob
+// CronJobStatus defines the observed state of CronJob.
type CronJobStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
-
- // A list of pointers to currently running jobs.
+ // active defines a list of pointers to currently running jobs.
// +optional
+ // +listType=atomic
+ // +kubebuilder:validation:MinItems=1
+ // +kubebuilder:validation:MaxItems=10
Active []corev1.ObjectReference `json:"active,omitempty"`
- // Information when was the last time the job was successfully scheduled.
+ // lastScheduleTime defines the information when was the last time the job was successfully scheduled.
// +optional
LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"`
-}
-//+kubebuilder:object:root=true
-//+kubebuilder:subresource:status
+ // For Kubernetes API conventions, see:
+ // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
+
+ // conditions represent the current state of the CronJob resource.
+ // Each condition has a unique type and reflects the status of a specific aspect of the resource.
+ //
+ // Standard condition types include:
+ // - "Available": the resource is fully functional
+ // - "Progressing": the resource is being created or updated
+ // - "Degraded": the resource failed to reach or maintain its desired state
+ //
+ // The status of each condition is one of True, False, or Unknown.
+ // +listType=map
+ // +listMapKey=type
+ // +optional
+ Conditions []metav1.Condition `json:"conditions,omitempty"`
+}
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+// +versionName=v2
// CronJob is the Schema for the cronjobs API
type CronJob struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
+ metav1.TypeMeta `json:",inline"`
+
+ // metadata is a standard object metadata
+ // +optional
+ metav1.ObjectMeta `json:"metadata,omitzero"`
- Spec CronJobSpec `json:"spec,omitempty"`
- Status CronJobStatus `json:"status,omitempty"`
+ // spec defines the desired state of CronJob
+ // +required
+ Spec CronJobSpec `json:"spec"`
+
+ // status defines the observed state of CronJob
+ // +optional
+ Status CronJobStatus `json:"status,omitzero"`
}
-//+kubebuilder:object:root=true
+// +kubebuilder:object:root=true
// CronJobList contains a list of CronJob
type CronJobList struct {
metav1.TypeMeta `json:",inline"`
- metav1.ListMeta `json:"metadata,omitempty"`
+ metav1.ListMeta `json:"metadata,omitzero"`
Items []CronJob `json:"items"`
}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_webhook.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_webhook.go
deleted file mode 100644
index ea5436bf9b9..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/cronjob_webhook.go
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
- ctrl "sigs.k8s.io/controller-runtime"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
-)
-
-// log is for logging in this package.
-var cronjoblog = logf.Log.WithName("cronjob-resource")
-
-func (r *CronJob) SetupWebhookWithManager(mgr ctrl.Manager) error {
- return ctrl.NewWebhookManagedBy(mgr).
- For(r).
- Complete()
-}
-
-// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
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 8448382dbed..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 2021 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.
@@ -13,11 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-// +kubebuilder:gen-docs:collapse=Apache License
-// Package v2 contains API Schema definitions for the batch v2 API group
-//+kubebuilder:object:generate=true
-//+groupName=batch.tutorial.kubebuilder.io
+// Package v2 contains API Schema definitions for the batch v2 API group.
+// +kubebuilder:object:generate=true
+// +groupName=batch.tutorial.kubebuilder.io
package v2
import (
@@ -26,10 +25,10 @@ import (
)
var (
- // GroupVersion is group version used to register these objects
+ // GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v2"}
- // SchemeBuilder is used to add go types to the GroupVersionKind scheme
+ // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/webhook_suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/api/v2/webhook_suite_test.go
deleted file mode 100644
index b1bbe50e5c4..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/api/v2/webhook_suite_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package v2
-
-import (
- "context"
- "crypto/tls"
- "fmt"
- "net"
- "path/filepath"
- "testing"
- "time"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
-
- admissionv1beta1 "k8s.io/api/admission/v1beta1"
- //+kubebuilder:scaffold:imports
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/client-go/rest"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-)
-
-// 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
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Webhook Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- ctx, cancel = context.WithCancel(context.TODO())
-
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
- ErrorIfCRDPathMissing: false,
- WebhookInstallOptions: envtest.WebhookInstallOptions{
- Paths: []string{filepath.Join("..", "..", "config", "webhook")},
- },
- }
-
- cfg, err := testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
-
- scheme := runtime.NewScheme()
- err = AddToScheme(scheme)
- Expect(err).NotTo(HaveOccurred())
-
- err = admissionv1beta1.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())
-
- // start webhook server using Manager
- webhookInstallOptions := &testEnv.WebhookInstallOptions
- mgr, err := ctrl.NewManager(cfg, ctrl.Options{
- Scheme: scheme,
- Host: webhookInstallOptions.LocalServingHost,
- Port: webhookInstallOptions.LocalServingPort,
- CertDir: webhookInstallOptions.LocalServingCertDir,
- LeaderElection: false,
- MetricsBindAddress: "0",
- })
- Expect(err).NotTo(HaveOccurred())
-
- err = (&CronJob{}).SetupWebhookWithManager(mgr)
- Expect(err).NotTo(HaveOccurred())
-
- //+kubebuilder:scaffold:webhook
-
- go func() {
- err = mgr.Start(ctx)
- if err != nil {
- Expect(err).NotTo(HaveOccurred())
- }
- }()
-
- // wait for the webhook server to get ready
- dialer := &net.Dialer{Timeout: time.Second}
- addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
- Eventually(func() error {
- conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
- if err != nil {
- return err
- }
- conn.Close()
- return nil
- }).Should(Succeed())
-
-}, 60)
-
-var _ = AfterSuite(func() {
- cancel()
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
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 8f4cf0e2b32..0a2dbce95de 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 @@
-// +build !ignore_autogenerated
+//go:build !ignore_autogenerated
/*
-Copyright 2021 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,6 +22,7 @@ package v2
import (
"k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -133,6 +134,13 @@ func (in *CronJobStatus) DeepCopyInto(out *CronJobStatus) {
in, out := &in.LastScheduleTime, &out.LastScheduleTime
*out = (*in).DeepCopy()
}
+ if in.Conditions != nil {
+ in, out := &in.Conditions, &out.Conditions
+ *out = make([]metav1.Condition, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJobStatus.
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go b/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go
new file mode 100644
index 00000000000..bf0f3f503ed
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go
@@ -0,0 +1,241 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+package main
+
+import (
+ "crypto/tls"
+ "flag"
+ "os"
+
+ // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
+ // to ensure that exec-entrypoint and run can make use of them.
+ _ "k8s.io/client-go/plugin/pkg/client/auth"
+
+ kbatchv1 "k8s.io/api/batch/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ 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/healthz"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/filters"
+ metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+ batchv2 "tutorial.kubebuilder.io/project/api/v2"
+ "tutorial.kubebuilder.io/project/internal/controller"
+ webhookv1 "tutorial.kubebuilder.io/project/internal/webhook/v1"
+ webhookv2 "tutorial.kubebuilder.io/project/internal/webhook/v2"
+ // +kubebuilder:scaffold:imports
+)
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+/*
+ */
+
+var (
+ scheme = runtime.NewScheme()
+ setupLog = ctrl.Log.WithName("setup")
+)
+
+func init() {
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+
+ utilruntime.Must(kbatchv1.AddToScheme(scheme)) // we've added this ourselves
+ utilruntime.Must(batchv1.AddToScheme(scheme))
+ utilruntime.Must(batchv2.AddToScheme(scheme))
+ // +kubebuilder:scaffold:scheme
+}
+
+// +kubebuilder:docs-gen:collapse=existing setup
+
+/*
+ */
+
+// 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
+ var enableHTTP2 bool
+ var tlsOpts []func(*tls.Config)
+ flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
+ flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
+ flag.BoolVar(&enableLeaderElection, "leader-elect", false,
+ "Enable leader election for controller manager. "+
+ "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{
+ Development: true,
+ }
+ opts.BindFlags(flag.CommandLine)
+ flag.Parse()
+
+ ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
+
+ // if the enable-http2 flag is false (the default), http/2 should be disabled
+ // due to its vulnerabilities. More specifically, disabling http/2 will
+ // prevent from being vulnerable to the HTTP/2 Stream Cancellation and
+ // Rapid Reset CVEs. For more information see:
+ // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
+ // - https://github.com/advisories/GHSA-4374-p667-p6c8
+ disableHTTP2 := func(c *tls.Config) {
+ setupLog.Info("disabling http/2")
+ c.NextProtos = []string{"http/1.1"}
+ }
+
+ if !enableHTTP2 {
+ tlsOpts = append(tlsOpts, disableHTTP2)
+ }
+
+ // Initial webhook TLS options
+ webhookTLSOpts := tlsOpts
+ webhookServerOptions := webhook.Options{
+ TLSOpts: webhookTLSOpts,
+ }
+
+ 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)
+
+ webhookServerOptions.CertDir = webhookCertPath
+ webhookServerOptions.CertName = webhookCertName
+ webhookServerOptions.KeyName = webhookCertKey
+ }
+
+ webhookServer := webhook.NewServer(webhookServerOptions)
+
+ // 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.22.4/pkg/metrics/server
+ // - https://book.kubebuilder.io/reference/metrics.html
+ metricsServerOptions := metricsserver.Options{
+ BindAddress: metricsAddr,
+ SecureServing: secureMetrics,
+ TLSOpts: tlsOpts,
+ }
+
+ if secureMetrics {
+ // 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.22.4/pkg/metrics/filters#WithAuthenticationAndAuthorization
+ metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
+ }
+
+ // 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)
+
+ metricsServerOptions.CertDir = metricsCertPath
+ metricsServerOptions.CertName = metricsCertName
+ metricsServerOptions.KeyName = metricsCertKey
+ }
+
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ Metrics: metricsServerOptions,
+ WebhookServer: webhookServer,
+ HealthProbeBindAddress: probeAddr,
+ LeaderElection: enableLeaderElection,
+ LeaderElectionID: "80807133.tutorial.kubebuilder.io",
+ // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
+ // when the Manager ends. This requires the binary to immediately end when the
+ // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
+ // speeds up voluntary leader transitions as the new leader don't have to wait
+ // LeaseDuration time first.
+ //
+ // In the default scaffold provided, the program ends immediately after
+ // the manager stops, so would be fine to enable this option. However,
+ // if you are doing or is intended to do any operation such as perform cleanups
+ // after the manager stops then its usage might be unsafe.
+ // LeaderElectionReleaseOnCancel: true,
+ })
+ if err != nil {
+ setupLog.Error(err, "unable to start manager")
+ os.Exit(1)
+ }
+
+ if err := (&controller.CronJobReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "CronJob")
+ os.Exit(1)
+ }
+
+ /*
+ Our existing call to SetupWebhookWithManager registers our conversion webhooks with the manager, too.
+ */
+ // nolint:goconst
+ if os.Getenv("ENABLE_WEBHOOKS") != "false" {
+ if err := webhookv1.SetupCronJobWebhookWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
+ os.Exit(1)
+ }
+ }
+ // nolint:goconst
+ if os.Getenv("ENABLE_WEBHOOKS") != "false" {
+ if err := webhookv2.SetupCronJobWebhookWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
+ os.Exit(1)
+ }
+ }
+ // +kubebuilder:scaffold:builder
+
+ if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up health check")
+ os.Exit(1)
+ }
+ if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up ready check")
+ os.Exit(1)
+ }
+
+ setupLog.Info("starting manager")
+ if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
+ setupLog.Error(err, "problem running manager")
+ os.Exit(1)
+ }
+}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager
deleted file mode 120000
index 938736bd894..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager
+++ /dev/null
@@ -1 +0,0 @@
-../../../../cronjob-tutorial/testdata/project/config/certmanager
\ No newline at end of file
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-webhook.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml
new file mode 100644
index 00000000000..ae025c9c6ed
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/certificate-webhook.yaml
@@ -0,0 +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
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ labels:
+ 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
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
new file mode 100644
index 00000000000..fcb7498e468
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomization.yaml
@@ -0,0 +1,7 @@
+resources:
+- issuer.yaml
+- certificate-webhook.yaml
+- certificate-metrics.yaml
+
+configurations:
+- kustomizeconfig.yaml
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
new file mode 100644
index 00000000000..cf6f89e8892
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/certmanager/kustomizeconfig.yaml
@@ -0,0 +1,8 @@
+# This configuration is for teaching kustomize how to update name ref substitution
+nameReference:
+- kind: Issuer
+ group: cert-manager.io
+ fieldSpecs:
+ - kind: Certificate
+ group: cert-manager.io
+ path: spec/issuerRef/name
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 776eeb0e17f..d4681701c4c 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
@@ -1,11 +1,9 @@
-
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.4.1
- creationTimestamp: null
+ controller-gen.kubebuilder.io/version: v0.19.0
name: cronjobs.batch.tutorial.kubebuilder.io
spec:
group: batch.tutorial.kubebuilder.io
@@ -19,287 +17,203 @@ spec:
- name: v1
schema:
openAPIV3Schema:
- description: CronJob is the Schema for the cronjobs API
properties:
apiVersion:
- description: 'APIVersion defines the versioned schema of this representation
- of an object. Servers should convert recognized schemas to the latest
- internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
- description: 'Kind is a string value representing the REST resource this
- object represents. Servers may infer this from the endpoint the client
- submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
- description: CronJobSpec defines the desired state of CronJob
properties:
concurrencyPolicy:
- description: 'Specifies how to treat concurrent executions of a Job.
- Valid values are: - "Allow" (default): allows CronJobs to run concurrently;
- - "Forbid": forbids concurrent runs, skipping next run if previous
- run hasn''t finished yet; - "Replace": cancels currently running
- job and replaces it with a new one'
+ default: Allow
enum:
- Allow
- Forbid
- Replace
type: string
failedJobsHistoryLimit:
- description: The number of failed finished jobs to retain. This is
- a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
jobTemplate:
- description: Specifies the job that will be created when executing
- a CronJob.
properties:
metadata:
- description: 'Standard object''s metadata of the jobs created
- from this template. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
type: object
spec:
- description: 'Specification of the desired behavior of the job.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
properties:
activeDeadlineSeconds:
- description: Specifies the duration in seconds relative to
- the startTime that the job may be active before the system
- tries to terminate it; value must be positive integer
format: int64
type: integer
backoffLimit:
- description: Specifies the number of retries before marking
- this job failed. Defaults to 6
format: int32
type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
completions:
- description: 'Specifies the desired number of successfully
- finished pods the job should be run with. Setting to nil
- means that the success of any pod signals the success of
- all pods, and allows parallelism to have any positive value. Setting
- to 1 means that parallelism is limited to 1 and the success
- of that pod signals the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
format: int32
type: integer
+ managedBy:
+ type: string
manualSelector:
- description: 'manualSelector controls generation of pod labels
- and pod selectors. Leave `manualSelector` unset unless you
- are certain what you are doing. When false or unset, the
- system pick labels unique to this job and appends those
- labels to the pod template. When true, the user is responsible
- for picking unique labels and specifying the selector. Failure
- to pick a unique label may cause this and other jobs to
- not function correctly. However, You may see `manualSelector=true`
- in jobs that were created with the old `extensions/v1beta1`
- API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector'
type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
parallelism:
- description: 'Specifies the maximum desired number of pods
- the job should run at any given time. The actual number
- of pods running in steady state will be less than this number
- when ((.spec.completions - .status.successful) < .spec.parallelism),
- i.e. when the work left to do is less than max parallelism.
- More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
format: int32
type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
selector:
- description: 'A label query over pods that should match the
- pod count. Normally, the system sets this field for you.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors'
properties:
matchExpressions:
- description: matchExpressions is a list of label selector
- requirements. The requirements are ANDed.
items:
- description: A label selector requirement is a selector
- that contains values, a key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the label key that the selector
- applies to.
type: string
operator:
- description: operator represents a key's relationship
- to a set of values. Valid operators are In, NotIn,
- Exists and DoesNotExist.
type: string
values:
- description: values is an array of string values.
- If the operator is In or NotIn, the values array
- must be non-empty. If the operator is Exists or
- DoesNotExist, the values array must be empty.
- This array is replaced during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map of {key,value} pairs.
- A single {key,value} in the matchLabels map is equivalent
- to an element of matchExpressions, whose key field is
- "key", the operator is "In", and the values array contains
- only "value". The requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
template:
- description: 'Describes the pod that will be created when
- executing a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
properties:
metadata:
- description: 'Standard object''s metadata. More info:
- https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
type: object
spec:
- description: 'Specification of the desired behavior of
- the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
properties:
activeDeadlineSeconds:
- description: Optional duration in seconds the pod
- may be active on the node relative to StartTime
- before the system will actively try to mark it failed
- and kill associated containers. Value must be a
- positive integer.
format: int64
type: integer
affinity:
- description: If specified, the pod's scheduling constraints
properties:
nodeAffinity:
- description: Describes node affinity scheduling
- rules for the pod.
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- affinity expressions specified by this field,
- but it may choose a node that violates one
- or more of the expressions. The node that
- is most preferred is the one with the greatest
- sum of weights, i.e. for each node that
- meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- affinity expressions, etc.), compute a sum
- by iterating through the elements of this
- field and adding "weight" to the sum if
- the node matches the corresponding matchExpressions;
- the node(s) with the highest sum are the
- most preferred.
items:
- description: An empty preferred scheduling
- term matches all objects with implicit
- weight 0 (i.e. it's a no-op). A null preferred
- scheduling term matches no objects (i.e.
- is also a no-op).
properties:
preference:
- description: A node selector term, associated
- with the corresponding weight.
properties:
matchExpressions:
- description: A list of node selector
- requirements by node's labels.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchFields:
- description: A list of node selector
- requirements by node's fields.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
+ x-kubernetes-map-type: atomic
weight:
- description: Weight associated with
- matching the corresponding nodeSelectorTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -307,244 +221,137 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the affinity requirements specified
- by this field cease to be met at some point
- during pod execution (e.g. due to an update),
- the system may or may not try to eventually
- evict the pod from its node.
properties:
nodeSelectorTerms:
- description: Required. A list of node
- selector terms. The terms are ORed.
items:
- description: A null or empty node selector
- term matches no objects. The requirements
- of them are ANDed. The TopologySelectorTerm
- type implements a subset of the NodeSelectorTerm.
properties:
matchExpressions:
- description: A list of node selector
- requirements by node's labels.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchFields:
- description: A list of node selector
- requirements by node's fields.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
+ x-kubernetes-map-type: atomic
type: array
+ x-kubernetes-list-type: atomic
required:
- nodeSelectorTerms
type: object
+ x-kubernetes-map-type: atomic
type: object
podAffinity:
- description: Describes pod affinity scheduling
- rules (e.g. co-locate this pod in the same node,
- zone, etc. as some other pod(s)).
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- affinity expressions specified by this field,
- but it may choose a node that violates one
- or more of the expressions. The node that
- is most preferred is the one with the greatest
- sum of weights, i.e. for each node that
- meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- affinity expressions, etc.), compute a sum
- by iterating through the elements of this
- field and adding "weight" to the sum if
- the node has pods which matches the corresponding
- podAffinityTerm; the node(s) with the highest
- sum are the most preferred.
items:
- description: The weights of all of the matched
- WeightedPodAffinityTerm fields are added
- per-node to find the most preferred node(s)
properties:
podAffinityTerm:
- description: Required. A pod affinity
- term, associated with the corresponding
- weight.
properties:
labelSelector:
- description: A label query over
- a set of resources, in this case
- pods.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies
- which namespaces the labelSelector
- applies to (matches against);
- null or empty list means "this
- pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be
- co-located (affinity) or not co-located
- (anti-affinity) with the pods
- matching the labelSelector in
- the specified namespaces, where
- co-located is defined as running
- on a node whose value of the label
- with key topologyKey matches that
- of any node on which any of the
- selected pods is running. Empty
- topologyKey is not allowed.
type: string
required:
- topologyKey
type: object
weight:
- description: weight associated with
- matching the corresponding podAffinityTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -552,240 +359,165 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the affinity requirements specified
- by this field cease to be met at some point
- during pod execution (e.g. due to a pod
- label update), the system may or may not
- try to eventually evict the pod from its
- node. When there are multiple elements,
- the lists of nodes corresponding to each
- podAffinityTerm are intersected, i.e. all
- terms must be satisfied.
items:
- description: Defines a set of pods (namely
- those matching the labelSelector relative
- to the given namespace(s)) that this pod
- should be co-located (affinity) or not
- co-located (anti-affinity) with, where
- co-located is defined as running on a
- node whose value of the label with key
- matches that of any node
- on which a pod of the set of pods is running
properties:
labelSelector:
- description: A label query over a set
- of resources, in this case pods.
properties:
matchExpressions:
- description: matchExpressions is
- a list of label selector requirements.
- The requirements are ANDed.
items:
- description: A label selector
- requirement is a selector that
- contains values, a key, and
- an operator that relates the
- key and values.
properties:
key:
- description: key is the label
- key that the selector applies
- to.
type: string
operator:
- description: operator represents
- a key's relationship to
- a set of values. Valid operators
- are In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is an
- array of string values.
- If the operator is In or
- NotIn, the values array
- must be non-empty. If the
- operator is Exists or DoesNotExist,
- the values array must be
- empty. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map
- of {key,value} pairs. A single
- {key,value} in the matchLabels
- map is equivalent to an element
- of matchExpressions, whose key
- field is "key", the operator is
- "In", and the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies which
- namespaces the labelSelector applies
- to (matches against); null or empty
- list means "this pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be co-located
- (affinity) or not co-located (anti-affinity)
- with the pods matching the labelSelector
- in the specified namespaces, where
- co-located is defined as running on
- a node whose value of the label with
- key topologyKey matches that of any
- node on which any of the selected
- pods is running. Empty topologyKey
- is not allowed.
type: string
required:
- topologyKey
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
podAntiAffinity:
- description: Describes pod anti-affinity scheduling
- rules (e.g. avoid putting this pod in the same
- node, zone, etc. as some other pod(s)).
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- anti-affinity expressions specified by this
- field, but it may choose a node that violates
- one or more of the expressions. The node
- that is most preferred is the one with the
- greatest sum of weights, i.e. for each node
- that meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- anti-affinity expressions, etc.), compute
- a sum by iterating through the elements
- of this field and adding "weight" to the
- sum if the node has pods which matches the
- corresponding podAffinityTerm; the node(s)
- with the highest sum are the most preferred.
items:
- description: The weights of all of the matched
- WeightedPodAffinityTerm fields are added
- per-node to find the most preferred node(s)
properties:
podAffinityTerm:
- description: Required. A pod affinity
- term, associated with the corresponding
- weight.
properties:
labelSelector:
- description: A label query over
- a set of resources, in this case
- pods.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies
- which namespaces the labelSelector
- applies to (matches against);
- null or empty list means "this
- pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be
- co-located (affinity) or not co-located
- (anti-affinity) with the pods
- matching the labelSelector in
- the specified namespaces, where
- co-located is defined as running
- on a node whose value of the label
- with key topologyKey matches that
- of any node on which any of the
- selected pods is running. Empty
- topologyKey is not allowed.
type: string
required:
- topologyKey
type: object
weight:
- description: weight associated with
- matching the corresponding podAffinityTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -793,764 +525,440 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the anti-affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the anti-affinity requirements
- specified by this field cease to be met
- at some point during pod execution (e.g.
- due to a pod label update), the system may
- or may not try to eventually evict the pod
- from its node. When there are multiple elements,
- the lists of nodes corresponding to each
- podAffinityTerm are intersected, i.e. all
- terms must be satisfied.
items:
- description: Defines a set of pods (namely
- those matching the labelSelector relative
- to the given namespace(s)) that this pod
- should be co-located (affinity) or not
- co-located (anti-affinity) with, where
- co-located is defined as running on a
- node whose value of the label with key
- matches that of any node
- on which a pod of the set of pods is running
properties:
labelSelector:
- description: A label query over a set
- of resources, in this case pods.
properties:
matchExpressions:
- description: matchExpressions is
- a list of label selector requirements.
- The requirements are ANDed.
items:
- description: A label selector
- requirement is a selector that
- contains values, a key, and
- an operator that relates the
- key and values.
properties:
key:
- description: key is the label
- key that the selector applies
- to.
type: string
operator:
- description: operator represents
- a key's relationship to
- a set of values. Valid operators
- are In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is an
- array of string values.
- If the operator is In or
- NotIn, the values array
- must be non-empty. If the
- operator is Exists or DoesNotExist,
- the values array must be
- empty. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map
- of {key,value} pairs. A single
- {key,value} in the matchLabels
- map is equivalent to an element
- of matchExpressions, whose key
- field is "key", the operator is
- "In", and the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies which
- namespaces the labelSelector applies
- to (matches against); null or empty
- list means "this pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be co-located
- (affinity) or not co-located (anti-affinity)
- with the pods matching the labelSelector
- in the specified namespaces, where
- co-located is defined as running on
- a node whose value of the label with
- key topologyKey matches that of any
- node on which any of the selected
- pods is running. Empty topologyKey
- is not allowed.
type: string
required:
- topologyKey
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
type: object
automountServiceAccountToken:
- description: AutomountServiceAccountToken indicates
- whether a service account token should be automatically
- mounted.
type: boolean
containers:
- description: List of containers belonging to the pod.
- Containers cannot currently be added or removed.
- There must be at least one container in a Pod. Cannot
- be updated.
items:
- description: A single application container that
- you want to run within a pod.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images
- This field is optional to allow higher level
- config management to default or override container
- images in workload controllers like Deployments
- and StatefulSets.'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Actions that the management system
- should take in response to container lifecycle
- events. Cannot be updated.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: 'Periodic probe of container liveness.
- Container will be restarted if the probe fails.
- Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the container specified
- as a DNS_LABEL. Each container in a pod must
- have a unique name (DNS_LABEL). Cannot be
- updated.
type: string
ports:
- description: List of ports to expose from the
- container. Exposing a port here gives the
- system additional information about the network
- connections a container uses, but is primarily
- informational. Not specifying a port here
- DOES NOT prevent that port from being exposed.
- Any port which is listening on the default
- "0.0.0.0" address inside a container will
- be accessible from the network. Cannot be
- updated.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
@@ -1561,145 +969,115 @@ spec:
- protocol
x-kubernetes-list-type: map
readinessProbe:
- description: 'Periodic probe of container service
- readiness. Container will be removed from
- service endpoints if the probe fails. Cannot
- be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: 'Compute Resources required by
- this container. Cannot be updated. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -1707,9 +1085,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -1718,1307 +1093,743 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: 'Security options the pod should
- run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
- More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: 'StartupProbe indicates that the
- Pod has successfully initialized. If specified,
- no other probes are executed until this completes
- successfully. If this probe fails, the Pod
- will be restarted, just as if the livenessProbe
- failed. This can be used to provide different
- probe parameters at the beginning of a Pod''s
- lifecycle, when it might take a long time
- to load data or warm a cache, than during
- steady-state operation. This cannot be updated.
- This is a beta feature enabled by the StartupProbe
- feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
dnsConfig:
- description: Specifies the DNS parameters of a pod.
- Parameters specified here will be merged to the
- generated DNS configuration based on DNSPolicy.
properties:
nameservers:
- description: A list of DNS name server IP addresses.
- This will be appended to the base nameservers
- generated from DNSPolicy. Duplicated nameservers
- will be removed.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
options:
- description: A list of DNS resolver options. This
- will be merged with the base options generated
- from DNSPolicy. Duplicated entries will be removed.
- Resolution options given in Options will override
- those that appear in the base DNSPolicy.
items:
- description: PodDNSConfigOption defines DNS
- resolver options of a pod.
properties:
name:
- description: Required.
type: string
value:
type: string
type: object
type: array
+ x-kubernetes-list-type: atomic
searches:
- description: A list of DNS search domains for
- host-name lookup. This will be appended to the
- base search paths generated from DNSPolicy.
- Duplicated search paths will be removed.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
dnsPolicy:
- description: Set DNS policy for the pod. Defaults
- to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet',
- 'ClusterFirst', 'Default' or 'None'. DNS parameters
- given in DNSConfig will be merged with the policy
- selected with DNSPolicy. To have DNS options set
- along with hostNetwork, you have to specify DNS
- policy explicitly to 'ClusterFirstWithHostNet'.
type: string
enableServiceLinks:
- description: 'EnableServiceLinks indicates whether
- information about services should be injected into
- pod''s environment variables, matching the syntax
- of Docker links. Optional: Defaults to true.'
type: boolean
ephemeralContainers:
- description: List of ephemeral containers run in this
- pod. Ephemeral containers may be run in an existing
- pod to perform user-initiated actions such as debugging.
- This list cannot be specified when creating a pod,
- and it cannot be modified by updating the pod spec.
- In order to add an ephemeral container to an existing
- pod, use the pod's ephemeralcontainers subresource.
- This field is alpha-level and is only honored by
- servers that enable the EphemeralContainers feature.
items:
- description: An EphemeralContainer is a container
- that may be added temporarily to an existing pod
- for user-initiated activities such as debugging.
- Ephemeral containers have no resource or scheduling
- guarantees, and they will not be restarted when
- they exit or when a pod is removed or restarted.
- If an ephemeral container causes a pod to exceed
- its resource allocation, the pod may be evicted.
- Ephemeral containers may not be added by directly
- updating the pod spec. They must be added via
- the pod's ephemeralcontainers subresource, and
- they will appear in the pod spec once added. This
- is an alpha feature enabled by the EphemeralContainers
- feature flag.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Lifecycle is not allowed for ephemeral
- containers.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the ephemeral container
- specified as a DNS_LABEL. This name must be
- unique among all containers, init containers
- and ephemeral containers.
type: string
ports:
- description: Ports are not allowed for ephemeral
- containers.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
readinessProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: Resources are not allowed for ephemeral
- containers. Ephemeral containers use spare
- resources already allocated to the pod.
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -3026,9 +1837,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -3037,1178 +1845,640 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: SecurityContext is not allowed
- for ephemeral containers.
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
targetContainerName:
- description: If set, the name of the container
- from PodSpec that this ephemeral container
- targets. The ephemeral container will be run
- in the namespaces (IPC, PID, etc) of this
- container. If not set then the ephemeral container
- is run in whatever namespaces are shared for
- the pod. Note that the container runtime must
- support this feature.
type: string
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
hostAliases:
- description: HostAliases is an optional list of hosts
- and IPs that will be injected into the pod's hosts
- file if specified. This is only valid for non-hostNetwork
- pods.
items:
- description: HostAlias holds the mapping between
- IP and hostnames that will be injected as an entry
- in the pod's hosts file.
properties:
hostnames:
- description: Hostnames for the above IP address.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
ip:
- description: IP address of the host file entry.
type: string
+ required:
+ - ip
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
hostIPC:
- description: 'Use the host''s ipc namespace. Optional:
- Default to false.'
type: boolean
hostNetwork:
- description: Host networking requested for this pod.
- Use the host's network namespace. If this option
- is set, the ports that will be used must be specified.
- Default to false.
type: boolean
hostPID:
- description: 'Use the host''s pid namespace. Optional:
- Default to false.'
+ type: boolean
+ hostUsers:
type: boolean
hostname:
- description: Specifies the hostname of the Pod If
- not specified, the pod's hostname will be set to
- a system-defined value.
+ type: string
+ hostnameOverride:
type: string
imagePullSecrets:
- description: 'ImagePullSecrets is an optional list
- of references to secrets in the same namespace to
- use for pulling any of the images used by this PodSpec.
- If specified, these secrets will be passed to individual
- puller implementations for them to use. For example,
- in the case of docker, only DockerConfig type secrets
- are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod'
items:
- description: LocalObjectReference contains enough
- information to let you locate the referenced object
- inside the same namespace.
properties:
name:
- description: 'Name of the referent. More info:
- https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
initContainers:
- description: 'List of initialization containers belonging
- to the pod. Init containers are executed in order
- prior to containers being started. If any init container
- fails, the pod is considered to have failed and
- is handled according to its restartPolicy. The name
- for an init container or normal container must be
- unique among all containers. Init containers may
- not have Lifecycle actions, Readiness probes, Liveness
- probes, or Startup probes. The resourceRequirements
- of an init container are taken into account during
- scheduling by finding the highest request/limit
- for each resource type, and then using the max of
- of that value or the sum of the normal containers.
- Limits are applied to init containers in a similar
- fashion. Init containers cannot currently be added
- or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/'
items:
- description: A single application container that
- you want to run within a pod.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images
- This field is optional to allow higher level
- config management to default or override container
- images in workload controllers like Deployments
- and StatefulSets.'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Actions that the management system
- should take in response to container lifecycle
- events. Cannot be updated.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- - port
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: 'Periodic probe of container liveness.
- Container will be restarted if the probe fails.
- Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the container specified
- as a DNS_LABEL. Each container in a pod must
- have a unique name (DNS_LABEL). Cannot be
- updated.
type: string
ports:
- description: List of ports to expose from the
- container. Exposing a port here gives the
- system additional information about the network
- connections a container uses, but is primarily
- informational. Not specifying a port here
- DOES NOT prevent that port from being exposed.
- Any port which is listening on the default
- "0.0.0.0" address inside a container will
- be accessible from the network. Cannot be
- updated.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
@@ -4219,145 +2489,115 @@ spec:
- protocol
x-kubernetes-list-type: map
readinessProbe:
- description: 'Periodic probe of container service
- readiness. Container will be removed from
- service endpoints if the probe fails. Cannot
- be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: 'Compute Resources required by
- this container. Cannot be updated. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -4365,9 +2605,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -4376,477 +2613,258 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: 'Security options the pod should
- run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
- More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: 'StartupProbe indicates that the
- Pod has successfully initialized. If specified,
- no other probes are executed until this completes
- successfully. If this probe fails, the Pod
- will be restarted, just as if the livenessProbe
- failed. This can be used to provide different
- probe parameters at the beginning of a Pod''s
- lifecycle, when it might take a long time
- to load data or warm a cache, than during
- steady-state operation. This cannot be updated.
- This is a beta feature enabled by the StartupProbe
- feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
nodeName:
- description: NodeName is a request to schedule this
- pod onto a specific node. If it is non-empty, the
- scheduler simply schedules this pod onto that node,
- assuming that it fits resource requirements.
type: string
nodeSelector:
additionalProperties:
type: string
- description: 'NodeSelector is a selector which must
- be true for the pod to fit on a node. Selector which
- must match a node''s labels for the pod to be scheduled
- on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/'
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
type: object
overhead:
additionalProperties:
@@ -4855,472 +2873,247 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Overhead represents the resource overhead
- associated with running a pod for a given RuntimeClass.
- This field will be autopopulated at admission time
- by the RuntimeClass admission controller. If the
- RuntimeClass admission controller is enabled, overhead
- must not be set in Pod create requests. The RuntimeClass
- admission controller will reject Pod create requests
- which have the overhead already set. If RuntimeClass
- is configured and selected in the PodSpec, Overhead
- will be set to the value defined in the corresponding
- RuntimeClass, otherwise it will remain unset and
- treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md
- This field is alpha-level as of Kubernetes v1.16,
- and is only honored by servers that enable the PodOverhead
- feature.'
type: object
preemptionPolicy:
- description: PreemptionPolicy is the Policy for preempting
- pods with lower priority. One of Never, PreemptLowerPriority.
- Defaults to PreemptLowerPriority if unset. This
- field is beta-level, gated by the NonPreemptingPriority
- feature-gate.
type: string
priority:
- description: The priority value. Various system components
- use this field to find the priority of the pod.
- When Priority Admission Controller is enabled, it
- prevents users from setting this field. The admission
- controller populates this field from PriorityClassName.
- The higher the value, the higher the priority.
format: int32
type: integer
priorityClassName:
- description: If specified, indicates the pod's priority.
- "system-node-critical" and "system-cluster-critical"
- are two special keywords which indicate the highest
- priorities with the former being the highest priority.
- Any other name must be defined by creating a PriorityClass
- object with that name. If not specified, the pod
- priority will be default or zero if there is no
- default.
type: string
readinessGates:
- description: 'If specified, all readiness gates will
- be evaluated for pod readiness. A pod is ready when
- all its containers are ready AND all conditions
- specified in the readiness gates have status equal
- to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md'
items:
- description: PodReadinessGate contains the reference
- to a pod condition
properties:
conditionType:
- description: ConditionType refers to a condition
- in the pod's condition list with matching
- type.
type: string
required:
- conditionType
type: object
type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
restartPolicy:
- description: 'Restart policy for all containers within
- the pod. One of Always, OnFailure, Never. Default
- to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy'
type: string
runtimeClassName:
- description: 'RuntimeClassName refers to a RuntimeClass
- object in the node.k8s.io group, which should be
- used to run this pod. If no RuntimeClass resource
- matches the named class, the pod will not be run.
- If unset or empty, the "legacy" RuntimeClass will
- be used, which is an implicit class with an empty
- definition that uses the default runtime handler.
- More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md
- This is a beta feature as of Kubernetes v1.14.'
type: string
schedulerName:
- description: If specified, the pod will be dispatched
- by specified scheduler. If not specified, the pod
- will be dispatched by default scheduler.
type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
securityContext:
- description: 'SecurityContext holds pod-level security
- attributes and common container settings. Optional:
- Defaults to empty. See type description for default
- values of each field.'
properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
fsGroup:
- description: "A special supplemental group that
- applies to all containers in a pod. Some volume
- types allow the Kubelet to change the ownership
- of that volume to be owned by the pod: \n 1.
- The owning GID will be the FSGroup 2. The setgid
- bit is set (new files created in the volume
- will be owned by FSGroup) 3. The permission
- bits are OR'd with rw-rw---- \n If unset, the
- Kubelet will not modify the ownership and permissions
- of any volume."
format: int64
type: integer
fsGroupChangePolicy:
- description: 'fsGroupChangePolicy defines behavior
- of changing ownership and permission of the
- volume before being exposed inside Pod. This
- field will only apply to volume types which
- support fsGroup based ownership(and permissions).
- It will have no effect on ephemeral volume types
- such as: secret, configmaps and emptydir. Valid
- values are "OnRootMismatch" and "Always". If
- not specified defaults to "Always".'
type: string
runAsGroup:
- description: The GID to run the entrypoint of
- the container process. Uses runtime default
- if unset. May also be set in SecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence for that container.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container must
- run as a non-root user. If true, the Kubelet
- will validate the image at runtime to ensure
- that it does not run as UID 0 (root) and fail
- to start the container if it does. If unset
- or false, no such validation will be performed.
- May also be set in SecurityContext. If set
- in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint of
- the container process. Defaults to user specified
- in image metadata if unspecified. May also be
- set in SecurityContext. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence for that
- container.
format: int64
type: integer
+ seLinuxChangePolicy:
+ type: string
seLinuxOptions:
- description: The SELinux context to be applied
- to all containers. If unspecified, the container
- runtime will allocate a random SELinux context
- for each container. May also be set in SecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence for that container.
properties:
level:
- description: Level is SELinux level label
- that applies to the container.
type: string
role:
- description: Role is a SELinux role label
- that applies to the container.
type: string
type:
- description: Type is a SELinux type label
- that applies to the container.
type: string
user:
- description: User is a SELinux user label
- that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use by the
- containers in this pod.
properties:
localhostProfile:
- description: localhostProfile indicates a
- profile defined in a file on the node should
- be used. The profile must be preconfigured
- on the node to work. Must be a descending
- path, relative to the kubelet's configured
- seccomp profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind of
- seccomp profile will be applied. Valid options
- are: \n Localhost - a profile defined in
- a file on the node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
supplementalGroups:
- description: A list of groups applied to the first
- process run in each container, in addition to
- the container's primary GID. If unspecified,
- no groups will be added to any container.
items:
format: int64
type: integer
type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
sysctls:
- description: Sysctls hold a list of namespaced
- sysctls used for the pod. Pods with unsupported
- sysctls (by the container runtime) might fail
- to launch.
items:
- description: Sysctl defines a kernel parameter
- to be set
properties:
name:
- description: Name of a property to set
type: string
value:
- description: Value of a property to set
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
windowsOptions:
- description: The Windows specific settings applied
- to all containers. If unspecified, the options
- within a container's SecurityContext will be
- used. If set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where the
- GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName is the
- name of the GMSA credential spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows to run
- the entrypoint of the container process.
- Defaults to the user specified in image
- metadata if unspecified. May also be set
- in PodSecurityContext. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
serviceAccount:
- description: 'DeprecatedServiceAccount is a depreciated
- alias for ServiceAccountName. Deprecated: Use serviceAccountName
- instead.'
type: string
serviceAccountName:
- description: 'ServiceAccountName is the name of the
- ServiceAccount to use to run this pod. More info:
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'
type: string
setHostnameAsFQDN:
- description: If true the pod's hostname will be configured
- as the pod's FQDN, rather than the leaf name (the
- default). In Linux containers, this means setting
- the FQDN in the hostname field of the kernel (the
- nodename field of struct utsname). In Windows containers,
- this means setting the registry value of hostname
- for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters
- to FQDN. If a pod does not have FQDN, this has no
- effect. Default to false.
type: boolean
shareProcessNamespace:
- description: 'Share a single process namespace between
- all of the containers in a pod. When this is set
- containers will be able to view and signal processes
- from other containers in the same pod, and the first
- process in each container will not be assigned PID
- 1. HostPID and ShareProcessNamespace cannot both
- be set. Optional: Default to false.'
type: boolean
subdomain:
- description: If specified, the fully qualified Pod
- hostname will be "...svc.". If not specified, the pod will not have
- a domainname at all.
type: string
terminationGracePeriodSeconds:
- description: Optional duration in seconds the pod
- needs to terminate gracefully. May be decreased
- in delete request. Value must be non-negative integer.
- The value zero indicates delete immediately. If
- this value is nil, the default grace period will
- be used instead. The grace period is the duration
- in seconds after the processes running in the pod
- are sent a termination signal and the time when
- the processes are forcibly halted with a kill signal.
- Set this value longer than the expected cleanup
- time for your process. Defaults to 30 seconds.
format: int64
type: integer
tolerations:
- description: If specified, the pod's tolerations.
items:
- description: The pod this Toleration is attached
- to tolerates any taint that matches the triple
- using the matching operator
- .
properties:
effect:
- description: Effect indicates the taint effect
- to match. Empty means match all taint effects.
- When specified, allowed values are NoSchedule,
- PreferNoSchedule and NoExecute.
type: string
key:
- description: Key is the taint key that the toleration
- applies to. Empty means match all taint keys.
- If the key is empty, operator must be Exists;
- this combination means to match all values
- and all keys.
type: string
operator:
- description: Operator represents a key's relationship
- to the value. Valid operators are Exists and
- Equal. Defaults to Equal. Exists is equivalent
- to wildcard for value, so that a pod can tolerate
- all taints of a particular category.
type: string
tolerationSeconds:
- description: TolerationSeconds represents the
- period of time the toleration (which must
- be of effect NoExecute, otherwise this field
- is ignored) tolerates the taint. By default,
- it is not set, which means tolerate the taint
- forever (do not evict). Zero and negative
- values will be treated as 0 (evict immediately)
- by the system.
format: int64
type: integer
value:
- description: Value is the taint value the toleration
- matches to. If the operator is Exists, the
- value should be empty, otherwise just a regular
- string.
type: string
type: object
type: array
+ x-kubernetes-list-type: atomic
topologySpreadConstraints:
- description: TopologySpreadConstraints describes how
- a group of pods ought to spread across topology
- domains. Scheduler will schedule pods in a way which
- abides by the constraints. All topologySpreadConstraints
- are ANDed.
items:
- description: TopologySpreadConstraint specifies
- how to spread matching pods among the given topology.
properties:
labelSelector:
- description: LabelSelector is used to find matching
- pods. Pods that match this label selector
- are counted to determine the number of pods
- in their corresponding topology domain.
properties:
matchExpressions:
- description: matchExpressions is a list
- of label selector requirements. The requirements
- are ANDed.
items:
- description: A label selector requirement
- is a selector that contains values,
- a key, and an operator that relates
- the key and values.
properties:
key:
- description: key is the label key
- that the selector applies to.
type: string
operator:
- description: operator represents a
- key's relationship to a set of values.
- Valid operators are In, NotIn, Exists
- and DoesNotExist.
type: string
values:
- description: values is an array of
- string values. If the operator is
- In or NotIn, the values array must
- be non-empty. If the operator is
- Exists or DoesNotExist, the values
- array must be empty. This array
- is replaced during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map of {key,value}
- pairs. A single {key,value} in the matchLabels
- map is equivalent to an element of matchExpressions,
- whose key field is "key", the operator
- is "In", and the values array contains
- only "value". The requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
maxSkew:
- description: 'MaxSkew describes the degree to
- which pods may be unevenly distributed. When
- `whenUnsatisfiable=DoNotSchedule`, it is the
- maximum permitted difference between the number
- of matching pods in the target topology and
- the global minimum. For example, in a 3-zone
- cluster, MaxSkew is set to 1, and pods with
- the same labelSelector spread as 1/1/0: |
- zone1 | zone2 | zone3 | | P | P | |
- - if MaxSkew is 1, incoming pod can only be
- scheduled to zone3 to become 1/1/1; scheduling
- it onto zone1(zone2) would make the ActualSkew(2-0)
- on zone1(zone2) violate MaxSkew(1). - if MaxSkew
- is 2, incoming pod can be scheduled onto any
- zone. When `whenUnsatisfiable=ScheduleAnyway`,
- it is used to give higher precedence to topologies
- that satisfy it. It''s a required field. Default
- value is 1 and 0 is not allowed.'
format: int32
type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
topologyKey:
- description: TopologyKey is the key of node
- labels. Nodes that have a label with this
- key and identical values are considered to
- be in the same topology. We consider each
- as a "bucket", and try to put
- balanced number of pods into each bucket.
- It's a required field.
type: string
whenUnsatisfiable:
- description: 'WhenUnsatisfiable indicates how
- to deal with a pod if it doesn''t satisfy
- the spread constraint. - DoNotSchedule (default)
- tells the scheduler not to schedule it. -
- ScheduleAnyway tells the scheduler to schedule
- the pod in any location, but giving higher
- precedence to topologies that would help reduce
- the skew. A constraint is considered "Unsatisfiable"
- for an incoming pod if and only if every possible
- node assigment for that pod would violate
- "MaxSkew" on some topology. For example, in
- a 3-zone cluster, MaxSkew is set to 1, and
- pods with the same labelSelector spread as
- 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P |
- If WhenUnsatisfiable is set to DoNotSchedule,
- incoming pod can only be scheduled to zone2(zone3)
- to become 3/2/1(3/1/2) as ActualSkew(2-1)
- on zone2(zone3) satisfies MaxSkew(1). In other
- words, the cluster can still be imbalanced,
- but scheduler won''t make it *more* imbalanced.
- It''s a required field.'
type: string
required:
- maxSkew
@@ -5333,590 +3126,244 @@ spec:
- whenUnsatisfiable
x-kubernetes-list-type: map
volumes:
- description: 'List of volumes that can be mounted
- by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes'
items:
- description: Volume represents a named volume in
- a pod that may be accessed by any container in
- the pod.
properties:
awsElasticBlockStore:
- description: 'AWSElasticBlockStore represents
- an AWS Disk resource that is attached to a
- kubelet''s host machine and then exposed to
- the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
partition:
- description: 'The partition in the volume
- that you want to mount. If omitted, the
- default is to mount by volume name. Examples:
- For volume /dev/sda1, you specify the
- partition as "1". Similarly, the volume
- partition for /dev/sda is "0" (or you
- can leave the property empty).'
format: int32
type: integer
readOnly:
- description: 'Specify "true" to force and
- set the ReadOnly property in VolumeMounts
- to "true". If omitted, the default is
- "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
type: boolean
volumeID:
- description: 'Unique ID of the persistent
- disk resource in AWS (Amazon EBS volume).
- More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
type: string
required:
- volumeID
type: object
azureDisk:
- description: AzureDisk represents an Azure Data
- Disk mount on the host and bind mount to the
- pod.
properties:
cachingMode:
- description: 'Host Caching mode: None, Read
- Only, Read Write.'
type: string
diskName:
- description: The Name of the data disk in
- the blob storage
type: string
diskURI:
- description: The URI the data disk in the
- blob storage
type: string
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
+ default: ext4
type: string
kind:
- description: 'Expected values Shared: multiple
- blob disks per storage account Dedicated:
- single blob disk per storage account Managed:
- azure managed data disk (only in managed
- availability set). defaults to shared'
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
+ default: false
type: boolean
required:
- diskName
- diskURI
type: object
azureFile:
- description: AzureFile represents an Azure File
- Service mount on the host and bind mount to
- the pod.
properties:
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretName:
- description: the name of secret that contains
- Azure Storage Account Name and Key
type: string
shareName:
- description: Share Name
type: string
required:
- secretName
- shareName
type: object
cephfs:
- description: CephFS represents a Ceph FS mount
- on the host that shares a pod's lifetime
properties:
monitors:
- description: 'Required: Monitors is a collection
- of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
path:
- description: 'Optional: Used as the mounted
- root, rather than the full Ceph tree,
- default is /'
type: string
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.
- More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: boolean
secretFile:
- description: 'Optional: SecretFile is the
- path to key ring for User, default is
- /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: string
secretRef:
- description: 'Optional: SecretRef is reference
- to the authentication secret for User,
- default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
user:
- description: 'Optional: User is the rados
- user name, default is admin More info:
- https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: string
required:
- monitors
type: object
cinder:
- description: 'Cinder represents a cinder volume
- attached and mounted on kubelets host machine.
- More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
properties:
fsType:
- description: 'Filesystem type to mount.
- Must be a filesystem type supported by
- the host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: string
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.
- More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: boolean
secretRef:
- description: 'Optional: points to a secret
- object containing parameters used to connect
- to OpenStack.'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
volumeID:
- description: 'volume id used to identify
- the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: string
required:
- volumeID
type: object
configMap:
- description: ConfigMap represents a configMap
- that should populate this volume
properties:
defaultMode:
- description: 'Optional: mode bits used to
- set permissions on created files by default.
- Must be an octal value between 0000 and
- 0777 or a decimal value between 0 and
- 511. YAML accepts both octal and decimal
- values, JSON requires decimal values for
- mode bits. Defaults to 0644. Directories
- within the path are not affected by this
- setting. This might be in conflict with
- other options that affect the file mode,
- like fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
items:
- description: If unspecified, each key-value
- pair in the Data field of the referenced
- ConfigMap will be projected into the volume
- as a file whose name is the key and content
- is the value. If specified, the listed
- keys will be projected into the specified
- paths, and unlisted keys will not be present.
- If a key is specified which is not present
- in the ConfigMap, the volume setup will
- error unless it is marked optional. Paths
- must be relative and may not contain the
- '..' path or start with '..'.
items:
- description: Maps a string key to a path
- within a volume.
properties:
key:
- description: The key to project.
type: string
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file. Must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: The relative path of
- the file to map the key to. May
- not be an absolute path. May not
- contain the path element '..'. May
- not start with the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent. More
- info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- or its keys must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
csi:
- description: CSI (Container Storage Interface)
- represents ephemeral storage that is handled
- by certain external CSI drivers (Beta feature).
properties:
driver:
- description: Driver is the name of the CSI
- driver that handles this volume. Consult
- with your admin for the correct name as
- registered in the cluster.
type: string
fsType:
- description: Filesystem type to mount. Ex.
- "ext4", "xfs", "ntfs". If not provided,
- the empty value is passed to the associated
- CSI driver which will determine the default
- filesystem to apply.
type: string
nodePublishSecretRef:
- description: NodePublishSecretRef is a reference
- to the secret object containing sensitive
- information to pass to the CSI driver
- to complete the CSI NodePublishVolume
- and NodeUnpublishVolume calls. This field
- is optional, and may be empty if no secret
- is required. If the secret object contains
- more than one secret, all secret references
- are passed.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
readOnly:
- description: Specifies a read-only configuration
- for the volume. Defaults to false (read/write).
type: boolean
volumeAttributes:
additionalProperties:
type: string
- description: VolumeAttributes stores driver-specific
- properties that are passed to the CSI
- driver. Consult your driver's documentation
- for supported values.
type: object
required:
- driver
type: object
downwardAPI:
- description: DownwardAPI represents downward
- API about the pod that should populate this
- volume
properties:
defaultMode:
- description: 'Optional: mode bits to use
- on created files by default. Must be a
- Optional: mode bits used to set permissions
- on created files by default. Must be an
- octal value between 0000 and 0777 or a
- decimal value between 0 and 511. YAML
- accepts both octal and decimal values,
- JSON requires decimal values for mode
- bits. Defaults to 0644. Directories within
- the path are not affected by this setting.
- This might be in conflict with other options
- that affect the file mode, like fsGroup,
- and the result can be other mode bits
- set.'
format: int32
type: integer
items:
- description: Items is a list of downward
- API volume file
items:
- description: DownwardAPIVolumeFile represents
- information to create the file containing
- the pod field
properties:
fieldRef:
- description: 'Required: Selects a
- field of the pod: only annotations,
- labels, name and namespace are supported.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file, must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: 'Required: Path is the
- relative path name of the file to
- be created. Must not be absolute
- or contain the ''..'' path. Must
- be utf-8 encoded. The first item
- of the relative path must not start
- with ''..'''
type: string
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- requests.cpu and requests.memory)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
required:
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
emptyDir:
- description: 'EmptyDir represents a temporary
- directory that shares a pod''s lifetime. More
- info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
properties:
medium:
- description: 'What type of storage medium
- should back this directory. The default
- is "" which means to use the node''s default
- medium. Must be an empty string (default)
- or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
type: string
sizeLimit:
anyOf:
- type: integer
- type: string
- description: 'Total amount of local storage
- required for this EmptyDir volume. The
- size limit is also applicable for memory
- medium. The maximum usage on memory medium
- EmptyDir would be the minimum value between
- the SizeLimit specified here and the sum
- of memory limits of all containers in
- a pod. The default is nil which means
- that the limit is undefined. More info:
- http://kubernetes.io/docs/user-guide/volumes#emptydir'
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
ephemeral:
- description: "Ephemeral represents a volume
- that is handled by a cluster storage driver
- (Alpha feature). The volume's lifecycle is
- tied to the pod that defines it - it will
- be created before the pod starts, and deleted
- when the pod is removed. \n Use this if: a)
- the volume is only needed while the pod runs,
- b) features of normal volumes like restoring
- from snapshot or capacity tracking are
- needed, c) the storage driver is specified
- through a storage class, and d) the storage
- driver supports dynamic volume provisioning
- through a PersistentVolumeClaim (see EphemeralVolumeSource
- for more information on the connection
- between this volume type and PersistentVolumeClaim).
- \n Use PersistentVolumeClaim or one of the
- vendor-specific APIs for volumes that persist
- for longer than the lifecycle of an individual
- pod. \n Use CSI for light-weight local ephemeral
- volumes if the CSI driver is meant to be used
- that way - see the documentation of the driver
- for more information. \n A pod can use both
- types of ephemeral volumes and persistent
- volumes at the same time."
properties:
- readOnly:
- description: Specifies a read-only configuration
- for the volume. Defaults to false (read/write).
- type: boolean
volumeClaimTemplate:
- description: "Will be used to create a stand-alone
- PVC to provision the volume. The pod in
- which this EphemeralVolumeSource is embedded
- will be the owner of the PVC, i.e. the
- PVC will be deleted together with the
- pod. The name of the PVC will be `-` where ``
- is the name from the `PodSpec.Volumes`
- array entry. Pod validation will reject
- the pod if the concatenated name is not
- valid for a PVC (for example, too long).
- \n An existing PVC with that name that
- is not owned by the pod will *not* be
- used for the pod to avoid using an unrelated
- volume by mistake. Starting the pod is
- then blocked until the unrelated PVC is
- removed. If such a pre-created PVC is
- meant to be used by the pod, the PVC has
- to updated with an owner reference to
- the pod once the pod exists. Normally
- this should not be necessary, but it may
- be useful when manually reconstructing
- a broken cluster. \n This field is read-only
- and no changes will be made by Kubernetes
- to the PVC after it has been created.
- \n Required, must not be nil."
properties:
metadata:
- description: May contain labels and
- annotations that will be copied into
- the PVC when creating it. No other
- fields are allowed and will be rejected
- during validation.
type: object
spec:
- description: The specification for the
- PersistentVolumeClaim. The entire
- content is copied unchanged into the
- PVC that gets created from this template.
- The same fields as in a PersistentVolumeClaim
- are also valid here.
properties:
accessModes:
- description: 'AccessModes contains
- the desired access modes the volume
- should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
dataSource:
- description: 'This field can be
- used to specify either: * An existing
- VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot
- - Beta) * An existing PVC (PersistentVolumeClaim)
- * An existing custom resource/object
- that implements data population
- (Alpha) In order to use VolumeSnapshot
- object types, the appropriate
- feature gate must be enabled (VolumeSnapshotDataSource
- or AnyVolumeDataSource) If the
- provisioner or an external controller
- can support the specified data
- source, it will create a new volume
- based on the contents of the specified
- data source. If the specified
- data source is not supported,
- the volume will not be created
- and the failure will be reported
- as an event. In the future, we
- plan to support more data source
- types and the behavior of the
- provisioner may change.'
properties:
apiGroup:
- description: APIGroup is the
- group for the resource being
- referenced. If APIGroup is
- not specified, the specified
- Kind must be in the core API
- group. For any other third-party
- types, APIGroup is required.
type: string
kind:
- description: Kind is the type
- of resource being referenced
type: string
name:
- description: Name is the name
- of resource being referenced
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
type: string
required:
- kind
- name
type: object
resources:
- description: 'Resources represents
- the minimum resources the volume
- should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
properties:
limits:
additionalProperties:
@@ -5925,10 +3372,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes
- the maximum amount of compute
- resources allowed. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -5937,96 +3380,41 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes
- the minimum amount of compute
- resources required. If Requests
- is omitted for a container,
- it defaults to Limits if that
- is explicitly specified, otherwise
- to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
selector:
- description: A label query over
- volumes to consider for binding.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
storageClassName:
- description: 'Name of the StorageClass
- required by the claim. More info:
- https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+ type: string
+ volumeAttributesClassName:
type: string
volumeMode:
- description: volumeMode defines
- what type of volume is required
- by the claim. Value of Filesystem
- is implied when not included in
- claim spec.
type: string
volumeName:
- description: VolumeName is the binding
- reference to the PersistentVolume
- backing this claim.
type: string
type: object
required:
@@ -6034,293 +3422,140 @@ spec:
type: object
type: object
fc:
- description: FC represents a Fibre Channel resource
- that is attached to a kubelet's host machine
- and then exposed to the pod.
properties:
fsType:
- description: 'Filesystem type to mount.
- Must be a filesystem type supported by
- the host operating system. Ex. "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. TODO: how do
- we prevent errors in the filesystem from
- compromising the machine'
type: string
lun:
- description: 'Optional: FC target lun number'
format: int32
type: integer
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.'
type: boolean
targetWWNs:
- description: 'Optional: FC target worldwide
- names (WWNs)'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
wwids:
- description: 'Optional: FC volume world
- wide identifiers (wwids) Either wwids
- or combination of targetWWNs and lun must
- be set, but not both simultaneously.'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
flexVolume:
- description: FlexVolume represents a generic
- volume resource that is provisioned/attached
- using an exec based plugin.
properties:
driver:
- description: Driver is the name of the driver
- to use for this volume.
type: string
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". The default filesystem depends
- on FlexVolume script.
type: string
options:
additionalProperties:
type: string
- description: 'Optional: Extra command options
- if any.'
type: object
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.'
type: boolean
secretRef:
- description: 'Optional: SecretRef is reference
- to the secret object containing sensitive
- information to pass to the plugin scripts.
- This may be empty if no secret object
- is specified. If the secret object contains
- more than one secret, all secrets are
- passed to the plugin scripts.'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
required:
- driver
type: object
flocker:
- description: Flocker represents a Flocker volume
- attached to a kubelet's host machine. This
- depends on the Flocker control service being
- running
properties:
datasetName:
- description: Name of the dataset stored
- as metadata -> name on the dataset for
- Flocker should be considered as deprecated
type: string
datasetUUID:
- description: UUID of the dataset. This is
- unique identifier of a Flocker dataset
type: string
type: object
gcePersistentDisk:
- description: 'GCEPersistentDisk represents a
- GCE Disk resource that is attached to a kubelet''s
- host machine and then exposed to the pod.
- More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
partition:
- description: 'The partition in the volume
- that you want to mount. If omitted, the
- default is to mount by volume name. Examples:
- For volume /dev/sda1, you specify the
- partition as "1". Similarly, the volume
- partition for /dev/sda is "0" (or you
- can leave the property empty). More info:
- https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
format: int32
type: integer
pdName:
- description: 'Unique name of the PD resource
- in GCE. Used to identify the disk in GCE.
- More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
type: string
readOnly:
- description: 'ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
type: boolean
required:
- pdName
type: object
gitRepo:
- description: 'GitRepo represents a git repository
- at a particular revision. DEPRECATED: GitRepo
- is deprecated. To provision a container with
- a git repo, mount an EmptyDir into an InitContainer
- that clones the repo using git, then mount
- the EmptyDir into the Pod''s container.'
properties:
directory:
- description: Target directory name. Must
- not contain or start with '..'. If '.'
- is supplied, the volume directory will
- be the git repository. Otherwise, if
- specified, the volume will contain the
- git repository in the subdirectory with
- the given name.
type: string
repository:
- description: Repository URL
type: string
revision:
- description: Commit hash for the specified
- revision.
type: string
required:
- repository
type: object
glusterfs:
- description: 'Glusterfs represents a Glusterfs
- mount on the host that shares a pod''s lifetime.
- More info: https://examples.k8s.io/volumes/glusterfs/README.md'
properties:
endpoints:
- description: 'EndpointsName is the endpoint
- name that details Glusterfs topology.
- More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: string
path:
- description: 'Path is the Glusterfs volume
- path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: string
readOnly:
- description: 'ReadOnly here will force the
- Glusterfs volume to be mounted with read-only
- permissions. Defaults to false. More info:
- https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: boolean
required:
- endpoints
- path
type: object
hostPath:
- description: 'HostPath represents a pre-existing
- file or directory on the host machine that
- is directly exposed to the container. This
- is generally used for system agents or other
- privileged things that are allowed to see
- the host machine. Most containers will NOT
- need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
- --- TODO(jonesdl) We need to restrict who
- can use host directory mounts and who can/can
- not mount host directories as read/write.'
properties:
path:
- description: 'Path of the directory on the
- host. If the path is a symlink, it will
- follow the link to the real path. More
- info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
type: string
type:
- description: 'Type for HostPath Volume Defaults
- to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
type: string
required:
- path
type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
iscsi:
- description: 'ISCSI represents an ISCSI Disk
- resource that is attached to a kubelet''s
- host machine and then exposed to the pod.
- More info: https://examples.k8s.io/volumes/iscsi/README.md'
properties:
chapAuthDiscovery:
- description: whether support iSCSI Discovery
- CHAP authentication
type: boolean
chapAuthSession:
- description: whether support iSCSI Session
- CHAP authentication
type: boolean
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
initiatorName:
- description: Custom iSCSI Initiator Name.
- If initiatorName is specified with iscsiInterface
- simultaneously, new iSCSI interface : will be created
- for the connection.
type: string
iqn:
- description: Target iSCSI Qualified Name.
type: string
iscsiInterface:
- description: iSCSI Interface Name that uses
- an iSCSI transport. Defaults to 'default'
- (tcp).
+ default: default
type: string
lun:
- description: iSCSI Target Lun number.
format: int32
type: integer
portals:
- description: iSCSI Target Portal List. The
- portal is either an IP or ip_addr:port
- if the port is other than default (typically
- TCP ports 860 and 3260).
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
readOnly:
- description: ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false.
type: boolean
secretRef:
- description: CHAP Secret for iSCSI target
- and initiator authentication
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
targetPortal:
- description: iSCSI Target Portal. The Portal
- is either an IP or ip_addr:port if the
- port is other than default (typically
- TCP ports 860 and 3260).
type: string
required:
- iqn
@@ -6328,577 +3563,301 @@ spec:
- targetPortal
type: object
name:
- description: 'Volume''s name. Must be a DNS_LABEL
- and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
nfs:
- description: 'NFS represents an NFS mount on
- the host that shares a pod''s lifetime More
- info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
properties:
path:
- description: 'Path that is exported by the
- NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: string
readOnly:
- description: 'ReadOnly here will force the
- NFS export to be mounted with read-only
- permissions. Defaults to false. More info:
- https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: boolean
server:
- description: 'Server is the hostname or
- IP address of the NFS server. More info:
- https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: string
required:
- path
- server
type: object
persistentVolumeClaim:
- description: 'PersistentVolumeClaimVolumeSource
- represents a reference to a PersistentVolumeClaim
- in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
properties:
claimName:
- description: 'ClaimName is the name of a
- PersistentVolumeClaim in the same namespace
- as the pod using this volume. More info:
- https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
type: string
readOnly:
- description: Will force the ReadOnly setting
- in VolumeMounts. Default false.
type: boolean
required:
- claimName
type: object
photonPersistentDisk:
- description: PhotonPersistentDisk represents
- a PhotonController persistent disk attached
- and mounted on kubelets host machine
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
pdID:
- description: ID that identifies Photon Controller
- persistent disk
type: string
required:
- pdID
type: object
portworxVolume:
- description: PortworxVolume represents a portworx
- volume attached and mounted on kubelets host
- machine
properties:
fsType:
- description: FSType represents the filesystem
- type to mount Must be a filesystem type
- supported by the host operating system.
- Ex. "ext4", "xfs". Implicitly inferred
- to be "ext4" if unspecified.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
volumeID:
- description: VolumeID uniquely identifies
- a Portworx volume
type: string
required:
- volumeID
type: object
projected:
- description: Items for all in one resources
- secrets, configmaps, and downward API
properties:
defaultMode:
- description: Mode bits used to set permissions
- on created files by default. Must be an
- octal value between 0000 and 0777 or a
- decimal value between 0 and 511. YAML
- accepts both octal and decimal values,
- JSON requires decimal values for mode
- bits. Directories within the path are
- not affected by this setting. This might
- be in conflict with other options that
- affect the file mode, like fsGroup, and
- the result can be other mode bits set.
format: int32
type: integer
sources:
- description: list of volume projections
items:
- description: Projection that may be projected
- along with other supported volume types
properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
configMap:
- description: information about the
- configMap data to project
properties:
items:
- description: If unspecified, each
- key-value pair in the Data field
- of the referenced ConfigMap
- will be projected into the volume
- as a file whose name is the
- key and content is the value.
- If specified, the listed keys
- will be projected into the specified
- paths, and unlisted keys will
- not be present. If a key is
- specified which is not present
- in the ConfigMap, the volume
- setup will error unless it is
- marked optional. Paths must
- be relative and may not contain
- the '..' path or start with
- '..'.
items:
- description: Maps a string key
- to a path within a volume.
properties:
key:
- description: The key to
- project.
type: string
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file.
- Must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: The relative
- path of the file to map
- the key to. May not be
- an absolute path. May
- not contain the path element
- '..'. May not start with
- the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its keys must be
- defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
downwardAPI:
- description: information about the
- downwardAPI data to project
properties:
items:
- description: Items is a list of
- DownwardAPIVolume file
items:
- description: DownwardAPIVolumeFile
- represents information to
- create the file containing
- the pod field
properties:
fieldRef:
- description: 'Required:
- Selects a field of the
- pod: only annotations,
- labels, name and namespace
- are supported.'
properties:
apiVersion:
- description: Version
- of the schema the
- FieldPath is written
- in terms of, defaults
- to "v1".
type: string
fieldPath:
- description: Path of
- the field to select
- in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file,
- must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: 'Required:
- Path is the relative
- path name of the file
- to be created. Must not
- be absolute or contain
- the ''..'' path. Must
- be utf-8 encoded. The
- first item of the relative
- path must not start with
- ''..'''
type: string
resourceFieldRef:
- description: 'Selects a
- resource of the container:
- only resources limits
- and requests (limits.cpu,
- limits.memory, requests.cpu
- and requests.memory) are
- currently supported.'
properties:
containerName:
- description: 'Container
- name: required for
- volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies
- the output format
- of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required:
- resource to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
required:
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
type: object
secret:
- description: information about the
- secret data to project
properties:
items:
- description: If unspecified, each
- key-value pair in the Data field
- of the referenced Secret will
- be projected into the volume
- as a file whose name is the
- key and content is the value.
- If specified, the listed keys
- will be projected into the specified
- paths, and unlisted keys will
- not be present. If a key is
- specified which is not present
- in the Secret, the volume setup
- will error unless it is marked
- optional. Paths must be relative
- and may not contain the '..'
- path or start with '..'.
items:
- description: Maps a string key
- to a path within a volume.
properties:
key:
- description: The key to
- project.
type: string
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file.
- Must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: The relative
- path of the file to map
- the key to. May not be
- an absolute path. May
- not contain the path element
- '..'. May not start with
- the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
serviceAccountToken:
- description: information about the
- serviceAccountToken data to project
properties:
audience:
- description: Audience is the intended
- audience of the token. A recipient
- of a token must identify itself
- with an identifier specified
- in the audience of the token,
- and otherwise should reject
- the token. The audience defaults
- to the identifier of the apiserver.
type: string
expirationSeconds:
- description: ExpirationSeconds
- is the requested duration of
- validity of the service account
- token. As the token approaches
- expiration, the kubelet volume
- plugin will proactively rotate
- the service account token. The
- kubelet will start trying to
- rotate the token if the token
- is older than 80 percent of
- its time to live or if the token
- is older than 24 hours.Defaults
- to 1 hour and must be at least
- 10 minutes.
format: int64
type: integer
path:
- description: Path is the path
- relative to the mount point
- of the file to project the token
- into.
type: string
required:
- path
type: object
type: object
type: array
- required:
- - sources
+ x-kubernetes-list-type: atomic
type: object
quobyte:
- description: Quobyte represents a Quobyte mount
- on the host that shares a pod's lifetime
properties:
group:
- description: Group to map volume access
- to Default is no group
type: string
readOnly:
- description: ReadOnly here will force the
- Quobyte volume to be mounted with read-only
- permissions. Defaults to false.
type: boolean
registry:
- description: Registry represents a single
- or multiple Quobyte Registry services
- specified as a string as host:port pair
- (multiple entries are separated with commas)
- which acts as the central registry for
- volumes
type: string
tenant:
- description: Tenant owning the given Quobyte
- volume in the Backend Used with dynamically
- provisioned Quobyte volumes, value is
- set by the plugin
type: string
user:
- description: User to map volume access to
- Defaults to serivceaccount user
type: string
volume:
- description: Volume is a string that references
- an already created Quobyte volume by name.
type: string
required:
- registry
- volume
type: object
rbd:
- description: 'RBD represents a Rados Block Device
- mount on the host that shares a pod''s lifetime.
- More info: https://examples.k8s.io/volumes/rbd/README.md'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
image:
- description: 'The rados image name. More
- info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
type: string
keyring:
- description: 'Keyring is the path to key
- ring for RBDUser. Default is /etc/ceph/keyring.
- More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: /etc/ceph/keyring
type: string
monitors:
- description: 'A collection of Ceph monitors.
- More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
pool:
- description: 'The rados pool name. Default
- is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: rbd
type: string
readOnly:
- description: 'ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
type: boolean
secretRef:
- description: 'SecretRef is name of the authentication
- secret for RBDUser. If provided overrides
- keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
user:
- description: 'The rados user name. Default
- is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: admin
type: string
required:
- image
- monitors
type: object
scaleIO:
- description: ScaleIO represents a ScaleIO persistent
- volume attached and mounted on Kubernetes
- nodes.
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Default is "xfs".
+ default: xfs
type: string
gateway:
- description: The host address of the ScaleIO
- API Gateway.
type: string
protectionDomain:
- description: The name of the ScaleIO Protection
- Domain for the configured storage.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretRef:
- description: SecretRef references to the
- secret for ScaleIO user and other sensitive
- information. If this is not provided,
- Login operation will fail.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
sslEnabled:
- description: Flag to enable/disable SSL
- communication with Gateway, default false
type: boolean
storageMode:
- description: Indicates whether the storage
- for a volume should be ThickProvisioned
- or ThinProvisioned. Default is ThinProvisioned.
+ default: ThinProvisioned
type: string
storagePool:
- description: The ScaleIO Storage Pool associated
- with the protection domain.
type: string
system:
- description: The name of the storage system
- as configured in ScaleIO.
type: string
volumeName:
- description: The name of a volume already
- created in the ScaleIO system that is
- associated with this volume source.
type: string
required:
- gateway
@@ -6906,154 +3865,58 @@ spec:
- system
type: object
secret:
- description: 'Secret represents a secret that
- should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
properties:
defaultMode:
- description: 'Optional: mode bits used to
- set permissions on created files by default.
- Must be an octal value between 0000 and
- 0777 or a decimal value between 0 and
- 511. YAML accepts both octal and decimal
- values, JSON requires decimal values for
- mode bits. Defaults to 0644. Directories
- within the path are not affected by this
- setting. This might be in conflict with
- other options that affect the file mode,
- like fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
items:
- description: If unspecified, each key-value
- pair in the Data field of the referenced
- Secret will be projected into the volume
- as a file whose name is the key and content
- is the value. If specified, the listed
- keys will be projected into the specified
- paths, and unlisted keys will not be present.
- If a key is specified which is not present
- in the Secret, the volume setup will error
- unless it is marked optional. Paths must
- be relative and may not contain the '..'
- path or start with '..'.
items:
- description: Maps a string key to a path
- within a volume.
properties:
key:
- description: The key to project.
type: string
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file. Must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: The relative path of
- the file to map the key to. May
- not be an absolute path. May not
- contain the path element '..'. May
- not start with the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
optional:
- description: Specify whether the Secret
- or its keys must be defined
type: boolean
secretName:
- description: 'Name of the secret in the
- pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
type: string
type: object
storageos:
- description: StorageOS represents a StorageOS
- volume attached and mounted on Kubernetes
- nodes.
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretRef:
- description: SecretRef specifies the secret
- to use for obtaining the StorageOS API
- credentials. If not specified, default
- values will be attempted.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
volumeName:
- description: VolumeName is the human-readable
- name of the StorageOS volume. Volume
- names are only unique within a namespace.
type: string
volumeNamespace:
- description: VolumeNamespace specifies the
- scope of the volume within StorageOS. If
- no namespace is specified then the Pod's
- namespace will be used. This allows the
- Kubernetes name scoping to be mirrored
- within StorageOS for tighter integration.
- Set VolumeName to any name to override
- the default behaviour. Set to "default"
- if you are not using namespaces within
- StorageOS. Namespaces that do not pre-exist
- within StorageOS will be created.
type: string
type: object
vsphereVolume:
- description: VsphereVolume represents a vSphere
- volume attached and mounted on kubelets host
- machine
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
storagePolicyID:
- description: Storage Policy Based Management
- (SPBM) profile ID associated with the
- StoragePolicyName.
type: string
storagePolicyName:
- description: Storage Policy Based Management
- (SPBM) profile name.
type: string
volumePath:
- description: Path that identifies vSphere
- volume vmdk
type: string
required:
- volumePath
@@ -7062,21 +3925,14 @@ spec:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
required:
- containers
type: object
type: object
ttlSecondsAfterFinished:
- description: ttlSecondsAfterFinished limits the lifetime of
- a Job that has finished execution (either Complete or Failed).
- If this field is set, ttlSecondsAfterFinished after the
- Job finishes, it is eligible to be automatically deleted.
- When the Job is being deleted, its lifecycle guarantees
- (e.g. finalizers) will be honored. If this field is unset,
- the Job won't be automatically deleted. If this field is
- set to zero, the Job becomes eligible to be deleted immediately
- after it finishes. This field is alpha-level and is only
- honored by servers that enable the TTLAfterFinished feature.
format: int32
type: integer
required:
@@ -7084,103 +3940,92 @@ spec:
type: object
type: object
schedule:
- description: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
minLength: 0
type: string
startingDeadlineSeconds:
- description: Optional deadline in seconds for starting the job if
- it misses scheduled time for any reason. Missed jobs executions
- will be counted as failed ones.
format: int64
minimum: 0
type: integer
successfulJobsHistoryLimit:
- description: The number of successful finished jobs to retain. This
- is a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
suspend:
- description: This flag tells the controller to suspend subsequent
- executions, it does not apply to already started executions. Defaults
- to false.
type: boolean
required:
- jobTemplate
- schedule
type: object
status:
- description: CronJobStatus defines the observed state of CronJob
properties:
active:
- description: A list of pointers to currently running jobs.
items:
- description: 'ObjectReference contains enough information to let
- you inspect or modify the referred object. --- New uses of this
- type are discouraged because of difficulty describing its usage
- when embedded in APIs. 1. Ignored fields. It includes many fields
- which are not generally honored. For instance, ResourceVersion
- and FieldPath are both very rarely valid in actual usage. 2.
- Invalid usage help. It is impossible to add specific help for
- individual usage. In most embedded usages, there are particular restrictions
- like, "must refer only to types A and B" or "UID not honored"
- or "name must be restricted". Those cannot be well described
- when embedded. 3. Inconsistent validation. Because the usages
- are different, the validation rules are different by usage, which
- makes it hard for users to predict what will happen. 4. The fields
- are both imprecise and overly precise. Kind is not a precise
- mapping to a URL. This can produce ambiguity during interpretation
- and require a REST mapping. In most cases, the dependency is
- on the group,resource tuple and the version of the actual
- struct is irrelevant. 5. We cannot easily change it. Because
- this type is embedded in many locations, updates to this type will
- affect numerous schemas. Don''t make new APIs embed an underspecified
- API type they do not control. Instead of using this type, create
- a locally provided and used type that is well-focused on your
- reference. For example, ServiceReferences for admission registration:
- https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533
- .'
properties:
apiVersion:
- description: API version of the referent.
type: string
fieldPath:
- description: 'If referring to a piece of an object instead of
- an entire object, this string should contain a valid JSON/Go
- field access statement, such as desiredState.manifest.containers[2].
- For example, if the object reference is to a container within
- a pod, this would take on a value like: "spec.containers{name}"
- (where "name" refers to the name of the container that triggered
- the event) or if no container name is specified "spec.containers[2]"
- (container with index 2 in this pod). This syntax is chosen
- only to have some well-defined way of referencing a part of
- an object. TODO: this design is not final and this field is
- subject to change in the future.'
type: string
kind:
- description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
- description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
- description: 'Specific resourceVersion to which this reference
- is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
lastScheduleTime:
- description: Information when was the last time the job was successfully
- scheduled.
format: date-time
type: string
type: object
+ required:
+ - spec
type: object
served: true
storage: true
@@ -7189,289 +4034,203 @@ spec:
- name: v2
schema:
openAPIV3Schema:
- description: CronJob is the Schema for the cronjobs API
properties:
apiVersion:
- description: 'APIVersion defines the versioned schema of this representation
- of an object. Servers should convert recognized schemas to the latest
- internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
- description: 'Kind is a string value representing the REST resource this
- object represents. Servers may infer this from the endpoint the client
- submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
- description: "We'll leave our spec largely unchanged, except to change
- the schedule field to a new type. \n CronJobSpec defines the desired
- state of CronJob"
properties:
concurrencyPolicy:
- description: 'Specifies how to treat concurrent executions of a Job.
- Valid values are: - "Allow" (default): allows CronJobs to run concurrently;
- - "Forbid": forbids concurrent runs, skipping next run if previous
- run hasn''t finished yet; - "Replace": cancels currently running
- job and replaces it with a new one'
+ default: Allow
enum:
- Allow
- Forbid
- Replace
type: string
failedJobsHistoryLimit:
- description: The number of failed finished jobs to retain. This is
- a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
jobTemplate:
- description: Specifies the job that will be created when executing
- a CronJob.
properties:
metadata:
- description: 'Standard object''s metadata of the jobs created
- from this template. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
type: object
spec:
- description: 'Specification of the desired behavior of the job.
- More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
properties:
activeDeadlineSeconds:
- description: Specifies the duration in seconds relative to
- the startTime that the job may be active before the system
- tries to terminate it; value must be positive integer
format: int64
type: integer
backoffLimit:
- description: Specifies the number of retries before marking
- this job failed. Defaults to 6
format: int32
type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
completions:
- description: 'Specifies the desired number of successfully
- finished pods the job should be run with. Setting to nil
- means that the success of any pod signals the success of
- all pods, and allows parallelism to have any positive value. Setting
- to 1 means that parallelism is limited to 1 and the success
- of that pod signals the success of the job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
format: int32
type: integer
+ managedBy:
+ type: string
manualSelector:
- description: 'manualSelector controls generation of pod labels
- and pod selectors. Leave `manualSelector` unset unless you
- are certain what you are doing. When false or unset, the
- system pick labels unique to this job and appends those
- labels to the pod template. When true, the user is responsible
- for picking unique labels and specifying the selector. Failure
- to pick a unique label may cause this and other jobs to
- not function correctly. However, You may see `manualSelector=true`
- in jobs that were created with the old `extensions/v1beta1`
- API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector'
type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
parallelism:
- description: 'Specifies the maximum desired number of pods
- the job should run at any given time. The actual number
- of pods running in steady state will be less than this number
- when ((.spec.completions - .status.successful) < .spec.parallelism),
- i.e. when the work left to do is less than max parallelism.
- More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
format: int32
type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
selector:
- description: 'A label query over pods that should match the
- pod count. Normally, the system sets this field for you.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors'
properties:
matchExpressions:
- description: matchExpressions is a list of label selector
- requirements. The requirements are ANDed.
items:
- description: A label selector requirement is a selector
- that contains values, a key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the label key that the selector
- applies to.
type: string
operator:
- description: operator represents a key's relationship
- to a set of values. Valid operators are In, NotIn,
- Exists and DoesNotExist.
type: string
values:
- description: values is an array of string values.
- If the operator is In or NotIn, the values array
- must be non-empty. If the operator is Exists or
- DoesNotExist, the values array must be empty.
- This array is replaced during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map of {key,value} pairs.
- A single {key,value} in the matchLabels map is equivalent
- to an element of matchExpressions, whose key field is
- "key", the operator is "In", and the values array contains
- only "value". The requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
template:
- description: 'Describes the pod that will be created when
- executing a job. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/'
properties:
metadata:
- description: 'Standard object''s metadata. More info:
- https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'
type: object
spec:
- description: 'Specification of the desired behavior of
- the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
properties:
activeDeadlineSeconds:
- description: Optional duration in seconds the pod
- may be active on the node relative to StartTime
- before the system will actively try to mark it failed
- and kill associated containers. Value must be a
- positive integer.
format: int64
type: integer
affinity:
- description: If specified, the pod's scheduling constraints
properties:
nodeAffinity:
- description: Describes node affinity scheduling
- rules for the pod.
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- affinity expressions specified by this field,
- but it may choose a node that violates one
- or more of the expressions. The node that
- is most preferred is the one with the greatest
- sum of weights, i.e. for each node that
- meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- affinity expressions, etc.), compute a sum
- by iterating through the elements of this
- field and adding "weight" to the sum if
- the node matches the corresponding matchExpressions;
- the node(s) with the highest sum are the
- most preferred.
items:
- description: An empty preferred scheduling
- term matches all objects with implicit
- weight 0 (i.e. it's a no-op). A null preferred
- scheduling term matches no objects (i.e.
- is also a no-op).
properties:
preference:
- description: A node selector term, associated
- with the corresponding weight.
properties:
matchExpressions:
- description: A list of node selector
- requirements by node's labels.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchFields:
- description: A list of node selector
- requirements by node's fields.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
+ x-kubernetes-map-type: atomic
weight:
- description: Weight associated with
- matching the corresponding nodeSelectorTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -7479,244 +4238,137 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the affinity requirements specified
- by this field cease to be met at some point
- during pod execution (e.g. due to an update),
- the system may or may not try to eventually
- evict the pod from its node.
properties:
nodeSelectorTerms:
- description: Required. A list of node
- selector terms. The terms are ORed.
items:
- description: A null or empty node selector
- term matches no objects. The requirements
- of them are ANDed. The TopologySelectorTerm
- type implements a subset of the NodeSelectorTerm.
properties:
matchExpressions:
- description: A list of node selector
- requirements by node's labels.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchFields:
- description: A list of node selector
- requirements by node's fields.
items:
- description: A node selector requirement
- is a selector that contains
- values, a key, and an operator
- that relates the key and values.
properties:
key:
- description: The label key
- that the selector applies
- to.
type: string
operator:
- description: Represents a
- key's relationship to a
- set of values. Valid operators
- are In, NotIn, Exists, DoesNotExist.
- Gt, and Lt.
type: string
values:
- description: An array of string
- values. If the operator
- is In or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the values
- array must be empty. If
- the operator is Gt or Lt,
- the values array must have
- a single element, which
- will be interpreted as an
- integer. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
+ x-kubernetes-map-type: atomic
type: array
+ x-kubernetes-list-type: atomic
required:
- nodeSelectorTerms
type: object
+ x-kubernetes-map-type: atomic
type: object
podAffinity:
- description: Describes pod affinity scheduling
- rules (e.g. co-locate this pod in the same node,
- zone, etc. as some other pod(s)).
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- affinity expressions specified by this field,
- but it may choose a node that violates one
- or more of the expressions. The node that
- is most preferred is the one with the greatest
- sum of weights, i.e. for each node that
- meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- affinity expressions, etc.), compute a sum
- by iterating through the elements of this
- field and adding "weight" to the sum if
- the node has pods which matches the corresponding
- podAffinityTerm; the node(s) with the highest
- sum are the most preferred.
items:
- description: The weights of all of the matched
- WeightedPodAffinityTerm fields are added
- per-node to find the most preferred node(s)
properties:
podAffinityTerm:
- description: Required. A pod affinity
- term, associated with the corresponding
- weight.
properties:
labelSelector:
- description: A label query over
- a set of resources, in this case
- pods.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies
- which namespaces the labelSelector
- applies to (matches against);
- null or empty list means "this
- pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be
- co-located (affinity) or not co-located
- (anti-affinity) with the pods
- matching the labelSelector in
- the specified namespaces, where
- co-located is defined as running
- on a node whose value of the label
- with key topologyKey matches that
- of any node on which any of the
- selected pods is running. Empty
- topologyKey is not allowed.
type: string
required:
- topologyKey
type: object
weight:
- description: weight associated with
- matching the corresponding podAffinityTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -7724,240 +4376,165 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the affinity requirements specified
- by this field cease to be met at some point
- during pod execution (e.g. due to a pod
- label update), the system may or may not
- try to eventually evict the pod from its
- node. When there are multiple elements,
- the lists of nodes corresponding to each
- podAffinityTerm are intersected, i.e. all
- terms must be satisfied.
items:
- description: Defines a set of pods (namely
- those matching the labelSelector relative
- to the given namespace(s)) that this pod
- should be co-located (affinity) or not
- co-located (anti-affinity) with, where
- co-located is defined as running on a
- node whose value of the label with key
- matches that of any node
- on which a pod of the set of pods is running
properties:
labelSelector:
- description: A label query over a set
- of resources, in this case pods.
properties:
matchExpressions:
- description: matchExpressions is
- a list of label selector requirements.
- The requirements are ANDed.
items:
- description: A label selector
- requirement is a selector that
- contains values, a key, and
- an operator that relates the
- key and values.
properties:
key:
- description: key is the label
- key that the selector applies
- to.
type: string
operator:
- description: operator represents
- a key's relationship to
- a set of values. Valid operators
- are In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is an
- array of string values.
- If the operator is In or
- NotIn, the values array
- must be non-empty. If the
- operator is Exists or DoesNotExist,
- the values array must be
- empty. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map
- of {key,value} pairs. A single
- {key,value} in the matchLabels
- map is equivalent to an element
- of matchExpressions, whose key
- field is "key", the operator is
- "In", and the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies which
- namespaces the labelSelector applies
- to (matches against); null or empty
- list means "this pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be co-located
- (affinity) or not co-located (anti-affinity)
- with the pods matching the labelSelector
- in the specified namespaces, where
- co-located is defined as running on
- a node whose value of the label with
- key topologyKey matches that of any
- node on which any of the selected
- pods is running. Empty topologyKey
- is not allowed.
type: string
required:
- topologyKey
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
podAntiAffinity:
- description: Describes pod anti-affinity scheduling
- rules (e.g. avoid putting this pod in the same
- node, zone, etc. as some other pod(s)).
properties:
preferredDuringSchedulingIgnoredDuringExecution:
- description: The scheduler will prefer to
- schedule pods to nodes that satisfy the
- anti-affinity expressions specified by this
- field, but it may choose a node that violates
- one or more of the expressions. The node
- that is most preferred is the one with the
- greatest sum of weights, i.e. for each node
- that meets all of the scheduling requirements
- (resource request, requiredDuringScheduling
- anti-affinity expressions, etc.), compute
- a sum by iterating through the elements
- of this field and adding "weight" to the
- sum if the node has pods which matches the
- corresponding podAffinityTerm; the node(s)
- with the highest sum are the most preferred.
items:
- description: The weights of all of the matched
- WeightedPodAffinityTerm fields are added
- per-node to find the most preferred node(s)
properties:
podAffinityTerm:
- description: Required. A pod affinity
- term, associated with the corresponding
- weight.
properties:
labelSelector:
- description: A label query over
- a set of resources, in this case
- pods.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies
- which namespaces the labelSelector
- applies to (matches against);
- null or empty list means "this
- pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be
- co-located (affinity) or not co-located
- (anti-affinity) with the pods
- matching the labelSelector in
- the specified namespaces, where
- co-located is defined as running
- on a node whose value of the label
- with key topologyKey matches that
- of any node on which any of the
- selected pods is running. Empty
- topologyKey is not allowed.
type: string
required:
- topologyKey
type: object
weight:
- description: weight associated with
- matching the corresponding podAffinityTerm,
- in the range 1-100.
format: int32
type: integer
required:
@@ -7965,764 +4542,440 @@ spec:
- weight
type: object
type: array
+ x-kubernetes-list-type: atomic
requiredDuringSchedulingIgnoredDuringExecution:
- description: If the anti-affinity requirements
- specified by this field are not met at scheduling
- time, the pod will not be scheduled onto
- the node. If the anti-affinity requirements
- specified by this field cease to be met
- at some point during pod execution (e.g.
- due to a pod label update), the system may
- or may not try to eventually evict the pod
- from its node. When there are multiple elements,
- the lists of nodes corresponding to each
- podAffinityTerm are intersected, i.e. all
- terms must be satisfied.
items:
- description: Defines a set of pods (namely
- those matching the labelSelector relative
- to the given namespace(s)) that this pod
- should be co-located (affinity) or not
- co-located (anti-affinity) with, where
- co-located is defined as running on a
- node whose value of the label with key
- matches that of any node
- on which a pod of the set of pods is running
properties:
labelSelector:
- description: A label query over a set
- of resources, in this case pods.
properties:
matchExpressions:
- description: matchExpressions is
- a list of label selector requirements.
- The requirements are ANDed.
items:
- description: A label selector
- requirement is a selector that
- contains values, a key, and
- an operator that relates the
- key and values.
properties:
key:
- description: key is the label
- key that the selector applies
- to.
type: string
operator:
- description: operator represents
- a key's relationship to
- a set of values. Valid operators
- are In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is an
- array of string values.
- If the operator is In or
- NotIn, the values array
- must be non-empty. If the
- operator is Exists or DoesNotExist,
- the values array must be
- empty. This array is replaced
- during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map
- of {key,value} pairs. A single
- {key,value} in the matchLabels
- map is equivalent to an element
- of matchExpressions, whose key
- field is "key", the operator is
- "In", and the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
namespaces:
- description: namespaces specifies which
- namespaces the labelSelector applies
- to (matches against); null or empty
- list means "this pod's namespace"
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
topologyKey:
- description: This pod should be co-located
- (affinity) or not co-located (anti-affinity)
- with the pods matching the labelSelector
- in the specified namespaces, where
- co-located is defined as running on
- a node whose value of the label with
- key topologyKey matches that of any
- node on which any of the selected
- pods is running. Empty topologyKey
- is not allowed.
type: string
required:
- topologyKey
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
type: object
automountServiceAccountToken:
- description: AutomountServiceAccountToken indicates
- whether a service account token should be automatically
- mounted.
type: boolean
containers:
- description: List of containers belonging to the pod.
- Containers cannot currently be added or removed.
- There must be at least one container in a Pod. Cannot
- be updated.
items:
- description: A single application container that
- you want to run within a pod.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images
- This field is optional to allow higher level
- config management to default or override container
- images in workload controllers like Deployments
- and StatefulSets.'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Actions that the management system
- should take in response to container lifecycle
- events. Cannot be updated.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: 'Periodic probe of container liveness.
- Container will be restarted if the probe fails.
- Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the container specified
- as a DNS_LABEL. Each container in a pod must
- have a unique name (DNS_LABEL). Cannot be
- updated.
type: string
ports:
- description: List of ports to expose from the
- container. Exposing a port here gives the
- system additional information about the network
- connections a container uses, but is primarily
- informational. Not specifying a port here
- DOES NOT prevent that port from being exposed.
- Any port which is listening on the default
- "0.0.0.0" address inside a container will
- be accessible from the network. Cannot be
- updated.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
@@ -8733,145 +4986,115 @@ spec:
- protocol
x-kubernetes-list-type: map
readinessProbe:
- description: 'Periodic probe of container service
- readiness. Container will be removed from
- service endpoints if the probe fails. Cannot
- be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: 'Compute Resources required by
- this container. Cannot be updated. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -8879,9 +5102,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -8890,1307 +5110,743 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: 'Security options the pod should
- run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
- More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: 'StartupProbe indicates that the
- Pod has successfully initialized. If specified,
- no other probes are executed until this completes
- successfully. If this probe fails, the Pod
- will be restarted, just as if the livenessProbe
- failed. This can be used to provide different
- probe parameters at the beginning of a Pod''s
- lifecycle, when it might take a long time
- to load data or warm a cache, than during
- steady-state operation. This cannot be updated.
- This is a beta feature enabled by the StartupProbe
- feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
dnsConfig:
- description: Specifies the DNS parameters of a pod.
- Parameters specified here will be merged to the
- generated DNS configuration based on DNSPolicy.
properties:
nameservers:
- description: A list of DNS name server IP addresses.
- This will be appended to the base nameservers
- generated from DNSPolicy. Duplicated nameservers
- will be removed.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
options:
- description: A list of DNS resolver options. This
- will be merged with the base options generated
- from DNSPolicy. Duplicated entries will be removed.
- Resolution options given in Options will override
- those that appear in the base DNSPolicy.
items:
- description: PodDNSConfigOption defines DNS
- resolver options of a pod.
properties:
name:
- description: Required.
type: string
value:
type: string
type: object
type: array
+ x-kubernetes-list-type: atomic
searches:
- description: A list of DNS search domains for
- host-name lookup. This will be appended to the
- base search paths generated from DNSPolicy.
- Duplicated search paths will be removed.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
dnsPolicy:
- description: Set DNS policy for the pod. Defaults
- to "ClusterFirst". Valid values are 'ClusterFirstWithHostNet',
- 'ClusterFirst', 'Default' or 'None'. DNS parameters
- given in DNSConfig will be merged with the policy
- selected with DNSPolicy. To have DNS options set
- along with hostNetwork, you have to specify DNS
- policy explicitly to 'ClusterFirstWithHostNet'.
type: string
enableServiceLinks:
- description: 'EnableServiceLinks indicates whether
- information about services should be injected into
- pod''s environment variables, matching the syntax
- of Docker links. Optional: Defaults to true.'
type: boolean
ephemeralContainers:
- description: List of ephemeral containers run in this
- pod. Ephemeral containers may be run in an existing
- pod to perform user-initiated actions such as debugging.
- This list cannot be specified when creating a pod,
- and it cannot be modified by updating the pod spec.
- In order to add an ephemeral container to an existing
- pod, use the pod's ephemeralcontainers subresource.
- This field is alpha-level and is only honored by
- servers that enable the EphemeralContainers feature.
items:
- description: An EphemeralContainer is a container
- that may be added temporarily to an existing pod
- for user-initiated activities such as debugging.
- Ephemeral containers have no resource or scheduling
- guarantees, and they will not be restarted when
- they exit or when a pod is removed or restarted.
- If an ephemeral container causes a pod to exceed
- its resource allocation, the pod may be evicted.
- Ephemeral containers may not be added by directly
- updating the pod spec. They must be added via
- the pod's ephemeralcontainers subresource, and
- they will appear in the pod spec once added. This
- is an alpha feature enabled by the EphemeralContainers
- feature flag.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Lifecycle is not allowed for ephemeral
- containers.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the ephemeral container
- specified as a DNS_LABEL. This name must be
- unique among all containers, init containers
- and ephemeral containers.
type: string
ports:
- description: Ports are not allowed for ephemeral
- containers.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
readinessProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: Resources are not allowed for ephemeral
- containers. Ephemeral containers use spare
- resources already allocated to the pod.
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -10198,9 +5854,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -10209,1178 +5862,640 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: SecurityContext is not allowed
- for ephemeral containers.
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: Probes are not allowed for ephemeral
- containers.
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
targetContainerName:
- description: If set, the name of the container
- from PodSpec that this ephemeral container
- targets. The ephemeral container will be run
- in the namespaces (IPC, PID, etc) of this
- container. If not set then the ephemeral container
- is run in whatever namespaces are shared for
- the pod. Note that the container runtime must
- support this feature.
type: string
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
hostAliases:
- description: HostAliases is an optional list of hosts
- and IPs that will be injected into the pod's hosts
- file if specified. This is only valid for non-hostNetwork
- pods.
items:
- description: HostAlias holds the mapping between
- IP and hostnames that will be injected as an entry
- in the pod's hosts file.
properties:
hostnames:
- description: Hostnames for the above IP address.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
ip:
- description: IP address of the host file entry.
type: string
+ required:
+ - ip
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
hostIPC:
- description: 'Use the host''s ipc namespace. Optional:
- Default to false.'
type: boolean
hostNetwork:
- description: Host networking requested for this pod.
- Use the host's network namespace. If this option
- is set, the ports that will be used must be specified.
- Default to false.
type: boolean
hostPID:
- description: 'Use the host''s pid namespace. Optional:
- Default to false.'
+ type: boolean
+ hostUsers:
type: boolean
hostname:
- description: Specifies the hostname of the Pod If
- not specified, the pod's hostname will be set to
- a system-defined value.
+ type: string
+ hostnameOverride:
type: string
imagePullSecrets:
- description: 'ImagePullSecrets is an optional list
- of references to secrets in the same namespace to
- use for pulling any of the images used by this PodSpec.
- If specified, these secrets will be passed to individual
- puller implementations for them to use. For example,
- in the case of docker, only DockerConfig type secrets
- are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod'
items:
- description: LocalObjectReference contains enough
- information to let you locate the referenced object
- inside the same namespace.
properties:
name:
- description: 'Name of the referent. More info:
- https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
initContainers:
- description: 'List of initialization containers belonging
- to the pod. Init containers are executed in order
- prior to containers being started. If any init container
- fails, the pod is considered to have failed and
- is handled according to its restartPolicy. The name
- for an init container or normal container must be
- unique among all containers. Init containers may
- not have Lifecycle actions, Readiness probes, Liveness
- probes, or Startup probes. The resourceRequirements
- of an init container are taken into account during
- scheduling by finding the highest request/limit
- for each resource type, and then using the max of
- of that value or the sum of the normal containers.
- Limits are applied to init containers in a similar
- fashion. Init containers cannot currently be added
- or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/'
items:
- description: A single application container that
- you want to run within a pod.
properties:
args:
- description: 'Arguments to the entrypoint. The
- docker image''s CMD is used if this is not
- provided. Variable references $(VAR_NAME)
- are expanded using the container''s environment.
- If a variable cannot be resolved, the reference
- in the input string will be unchanged. The
- $(VAR_NAME) syntax can be escaped with a double
- $$, ie: $$(VAR_NAME). Escaped references will
- never be expanded, regardless of whether the
- variable exists or not. Cannot be updated.
- More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
command:
- description: 'Entrypoint array. Not executed
- within a shell. The docker image''s ENTRYPOINT
- is used if this is not provided. Variable
- references $(VAR_NAME) are expanded using
- the container''s environment. If a variable
- cannot be resolved, the reference in the input
- string will be unchanged. The $(VAR_NAME)
- syntax can be escaped with a double $$, ie:
- $$(VAR_NAME). Escaped references will never
- be expanded, regardless of whether the variable
- exists or not. Cannot be updated. More info:
- https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
env:
- description: List of environment variables to
- set in the container. Cannot be updated.
items:
- description: EnvVar represents an environment
- variable present in a Container.
properties:
name:
- description: Name of the environment variable.
- Must be a C_IDENTIFIER.
type: string
value:
- description: 'Variable references $(VAR_NAME)
- are expanded using the previous defined
- environment variables in the container
- and any service environment variables.
- If a variable cannot be resolved, the
- reference in the input string will be
- unchanged. The $(VAR_NAME) syntax can
- be escaped with a double $$, ie: $$(VAR_NAME).
- Escaped references will never be expanded,
- regardless of whether the variable exists
- or not. Defaults to "".'
type: string
valueFrom:
- description: Source for the environment
- variable's value. Cannot be used if
- value is not empty.
properties:
configMapKeyRef:
- description: Selects a key of a ConfigMap.
properties:
key:
- description: The key to select.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its key must be
- defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
fieldRef:
- description: 'Selects a field of the
- pod: supports metadata.name, metadata.namespace,
- `metadata.labels['''']`, `metadata.annotations['''']`,
- spec.nodeName, spec.serviceAccountName,
- status.hostIP, status.podIP, status.podIPs.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- limits.ephemeral-storage, requests.cpu,
- requests.memory and requests.ephemeral-storage)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
secretKeyRef:
- description: Selects a key of a secret
- in the pod's namespace
properties:
key:
- description: The key of the secret
- to select from. Must be a valid
- secret key.
type: string
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
required:
- key
type: object
+ x-kubernetes-map-type: atomic
type: object
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
envFrom:
- description: List of sources to populate environment
- variables in the container. The keys defined
- within a source must be a C_IDENTIFIER. All
- invalid keys will be reported as an event
- when the container is starting. When a key
- exists in multiple sources, the value associated
- with the last source will take precedence.
- Values defined by an Env with a duplicate
- key will take precedence. Cannot be updated.
items:
- description: EnvFromSource represents the
- source of a set of ConfigMaps
properties:
configMapRef:
- description: The ConfigMap to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
prefix:
- description: An optional identifier to
- prepend to each key in the ConfigMap.
- Must be a C_IDENTIFIER.
type: string
secretRef:
- description: The Secret to select from
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the Secret
- must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
type: object
type: array
+ x-kubernetes-list-type: atomic
image:
- description: 'Docker image name. More info:
- https://kubernetes.io/docs/concepts/containers/images
- This field is optional to allow higher level
- config management to default or override container
- images in workload controllers like Deployments
- and StatefulSets.'
type: string
imagePullPolicy:
- description: 'Image pull policy. One of Always,
- Never, IfNotPresent. Defaults to Always if
- :latest tag is specified, or IfNotPresent
- otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'
type: string
lifecycle:
- description: Actions that the management system
- should take in response to container lifecycle
- events. Cannot be updated.
properties:
postStart:
- description: 'PostStart is called immediately
- after a container is created. If the handler
- fails, the container is terminated and
- restarted according to its restart policy.
- Other management of the container blocks
- until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
preStop:
- description: 'PreStop is called immediately
- before a container is terminated due to
- an API request or management event such
- as liveness/startup probe failure, preemption,
- resource contention, etc. The handler
- is not called if the container crashes
- or exits. The reason for termination is
- passed to the handler. The Pod''s termination
- grace period countdown begins before the
- PreStop hooked is executed. Regardless
- of the outcome of the handler, the container
- will eventually terminate within the Pod''s
- termination grace period. Other management
- of the container blocks until the hook
- completes or until the termination grace
- period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
properties:
exec:
- description: One and only one of the
- following should be specified. Exec
- specifies the action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the
- command is root ('/') in the
- container's filesystem. The command
- is simply exec'd, it is not run
- inside a shell, so traditional
- shell instructions ('|', etc)
- won't work. To use a shell, you
- need to explicitly call out to
- that shell. Exit status of 0 is
- treated as live/healthy and non-zero
- is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect
- to, defaults to the pod IP. You
- probably want to set "Host" in
- httpHeaders instead.
type: string
httpHeaders:
- description: Custom headers to set
- in the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in
- HTTP probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the
- HTTP server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
tcpSocket:
- description: 'TCPSocket specifies an
- action involving a TCP port. TCP hooks
- not yet supported TODO: implement
- a realistic TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name
- to connect to, defaults to the
- pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the
- port to access on the container.
- Number must be in the range 1
- to 65535. Name must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
type: object
+ stopSignal:
+ type: string
type: object
livenessProbe:
- description: 'Periodic probe of container liveness.
- Container will be restarted if the probe fails.
- Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
name:
- description: Name of the container specified
- as a DNS_LABEL. Each container in a pod must
- have a unique name (DNS_LABEL). Cannot be
- updated.
type: string
ports:
- description: List of ports to expose from the
- container. Exposing a port here gives the
- system additional information about the network
- connections a container uses, but is primarily
- informational. Not specifying a port here
- DOES NOT prevent that port from being exposed.
- Any port which is listening on the default
- "0.0.0.0" address inside a container will
- be accessible from the network. Cannot be
- updated.
items:
- description: ContainerPort represents a network
- port in a single container.
properties:
containerPort:
- description: Number of port to expose
- on the pod's IP address. This must be
- a valid port number, 0 < x < 65536.
format: int32
type: integer
hostIP:
- description: What host IP to bind the
- external port to.
type: string
hostPort:
- description: Number of port to expose
- on the host. If specified, this must
- be a valid port number, 0 < x < 65536.
- If HostNetwork is specified, this must
- match ContainerPort. Most containers
- do not need this.
format: int32
type: integer
name:
- description: If specified, this must be
- an IANA_SVC_NAME and unique within the
- pod. Each named port in a pod must have
- a unique name. Name for the port that
- can be referred to by services.
type: string
protocol:
default: TCP
- description: Protocol for port. Must be
- UDP, TCP, or SCTP. Defaults to "TCP".
type: string
required:
- containerPort
@@ -11391,145 +6506,115 @@ spec:
- protocol
x-kubernetes-list-type: map
readinessProbe:
- description: 'Periodic probe of container service
- readiness. Container will be removed from
- service endpoints if the probe fails. Cannot
- be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
resources:
- description: 'Compute Resources required by
- this container. Cannot be updated. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
limits:
additionalProperties:
anyOf:
@@ -11537,9 +6622,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum
- amount of compute resources allowed. More
- info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -11548,477 +6630,258 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum
- amount of compute resources required.
- If Requests is omitted for a container,
- it defaults to Limits if that is explicitly
- specified, otherwise to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
securityContext:
- description: 'Security options the pod should
- run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/
- More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'
properties:
allowPrivilegeEscalation:
- description: 'AllowPrivilegeEscalation controls
- whether a process can gain more privileges
- than its parent process. This bool directly
- controls if the no_new_privs flag will
- be set on the container process. AllowPrivilegeEscalation
- is true always when the container is:
- 1) run as Privileged 2) has CAP_SYS_ADMIN'
type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
capabilities:
- description: The capabilities to add/drop
- when running containers. Defaults to the
- default set of capabilities granted by
- the container runtime.
properties:
add:
- description: Added capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
drop:
- description: Removed capabilities
items:
- description: Capability represent
- POSIX capabilities type
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
privileged:
- description: Run container in privileged
- mode. Processes in privileged containers
- are essentially equivalent to root on
- the host. Defaults to false.
type: boolean
procMount:
- description: procMount denotes the type
- of proc mount to use for the containers.
- The default is DefaultProcMount which
- uses the container runtime defaults for
- readonly paths and masked paths. This
- requires the ProcMountType feature flag
- to be enabled.
type: string
readOnlyRootFilesystem:
- description: Whether this container has
- a read-only root filesystem. Default is
- false.
type: boolean
runAsGroup:
- description: The GID to run the entrypoint
- of the container process. Uses runtime
- default if unset. May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container
- must run as a non-root user. If true,
- the Kubelet will validate the image at
- runtime to ensure that it does not run
- as UID 0 (root) and fail to start the
- container if it does. If unset or false,
- no such validation will be performed.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint
- of the container process. Defaults to
- user specified in image metadata if unspecified.
- May also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
format: int64
type: integer
seLinuxOptions:
- description: The SELinux context to be applied
- to the container. If unspecified, the
- container runtime will allocate a random
- SELinux context for each container. May
- also be set in PodSecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext
- takes precedence.
properties:
level:
- description: Level is SELinux level
- label that applies to the container.
type: string
role:
- description: Role is a SELinux role
- label that applies to the container.
type: string
type:
- description: Type is a SELinux type
- label that applies to the container.
type: string
user:
- description: User is a SELinux user
- label that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use
- by this container. If seccomp options
- are provided at both the pod & container
- level, the container options override
- the pod options.
properties:
localhostProfile:
- description: localhostProfile indicates
- a profile defined in a file on the
- node should be used. The profile must
- be preconfigured on the node to work.
- Must be a descending path, relative
- to the kubelet's configured seccomp
- profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind
- of seccomp profile will be applied.
- Valid options are: \n Localhost -
- a profile defined in a file on the
- node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
windowsOptions:
- description: The Windows specific settings
- applied to all containers. If unspecified,
- the options from the PodSecurityContext
- will be used. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where
- the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName
- is the name of the GMSA credential
- spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows
- to run the entrypoint of the container
- process. Defaults to the user specified
- in image metadata if unspecified.
- May also be set in PodSecurityContext.
- If set in both SecurityContext and
- PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
startupProbe:
- description: 'StartupProbe indicates that the
- Pod has successfully initialized. If specified,
- no other probes are executed until this completes
- successfully. If this probe fails, the Pod
- will be restarted, just as if the livenessProbe
- failed. This can be used to provide different
- probe parameters at the beginning of a Pod''s
- lifecycle, when it might take a long time
- to load data or warm a cache, than during
- steady-state operation. This cannot be updated.
- This is a beta feature enabled by the StartupProbe
- feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
properties:
exec:
- description: One and only one of the following
- should be specified. Exec specifies the
- action to take.
properties:
command:
- description: Command is the command
- line to execute inside the container,
- the working directory for the command is
- root ('/') in the container's filesystem.
- The command is simply exec'd, it is
- not run inside a shell, so traditional
- shell instructions ('|', etc) won't
- work. To use a shell, you need to
- explicitly call out to that shell.
- Exit status of 0 is treated as live/healthy
- and non-zero is unhealthy.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
failureThreshold:
- description: Minimum consecutive failures
- for the probe to be considered failed
- after having succeeded. Defaults to 3.
- Minimum value is 1.
format: int32
type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
httpGet:
- description: HTTPGet specifies the http
- request to perform.
properties:
host:
- description: Host name to connect to,
- defaults to the pod IP. You probably
- want to set "Host" in httpHeaders
- instead.
type: string
httpHeaders:
- description: Custom headers to set in
- the request. HTTP allows repeated
- headers.
items:
- description: HTTPHeader describes
- a custom header to be used in HTTP
- probes
properties:
name:
- description: The header field
- name
type: string
value:
- description: The header field
- value
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
path:
- description: Path to access on the HTTP
- server.
type: string
port:
anyOf:
- type: integer
- type: string
- description: Name or number of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
scheme:
- description: Scheme to use for connecting
- to the host. Defaults to HTTP.
type: string
required:
- port
type: object
initialDelaySeconds:
- description: 'Number of seconds after the
- container has started before liveness
- probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
periodSeconds:
- description: How often (in seconds) to perform
- the probe. Default to 10 seconds. Minimum
- value is 1.
format: int32
type: integer
successThreshold:
- description: Minimum consecutive successes
- for the probe to be considered successful
- after having failed. Defaults to 1. Must
- be 1 for liveness and startup. Minimum
- value is 1.
format: int32
type: integer
tcpSocket:
- description: 'TCPSocket specifies an action
- involving a TCP port. TCP hooks not yet
- supported TODO: implement a realistic
- TCP lifecycle hook'
properties:
host:
- description: 'Optional: Host name to
- connect to, defaults to the pod IP.'
type: string
port:
anyOf:
- type: integer
- type: string
- description: Number or name of the port
- to access on the container. Number
- must be in the range 1 to 65535. Name
- must be an IANA_SVC_NAME.
x-kubernetes-int-or-string: true
required:
- port
type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
timeoutSeconds:
- description: 'Number of seconds after which
- the probe times out. Defaults to 1 second.
- Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'
format: int32
type: integer
type: object
stdin:
- description: Whether this container should allocate
- a buffer for stdin in the container runtime.
- If this is not set, reads from stdin in the
- container will always result in EOF. Default
- is false.
type: boolean
stdinOnce:
- description: Whether the container runtime should
- close the stdin channel after it has been
- opened by a single attach. When stdin is true
- the stdin stream will remain open across multiple
- attach sessions. If stdinOnce is set to true,
- stdin is opened on container start, is empty
- until the first client attaches to stdin,
- and then remains open and accepts data until
- the client disconnects, at which time stdin
- is closed and remains closed until the container
- is restarted. If this flag is false, a container
- processes that reads from stdin will never
- receive an EOF. Default is false
type: boolean
terminationMessagePath:
- description: 'Optional: Path at which the file
- to which the container''s termination message
- will be written is mounted into the container''s
- filesystem. Message written is intended to
- be brief final status, such as an assertion
- failure message. Will be truncated by the
- node if greater than 4096 bytes. The total
- message length across all containers will
- be limited to 12kb. Defaults to /dev/termination-log.
- Cannot be updated.'
type: string
terminationMessagePolicy:
- description: Indicate how the termination message
- should be populated. File will use the contents
- of terminationMessagePath to populate the
- container status message on both success and
- failure. FallbackToLogsOnError will use the
- last chunk of container log output if the
- termination message file is empty and the
- container exited with an error. The log output
- is limited to 2048 bytes or 80 lines, whichever
- is smaller. Defaults to File. Cannot be updated.
type: string
tty:
- description: Whether this container should allocate
- a TTY for itself, also requires 'stdin' to
- be true. Default is false.
type: boolean
volumeDevices:
- description: volumeDevices is the list of block
- devices to be used by the container.
items:
- description: volumeDevice describes a mapping
- of a raw block device within a container.
properties:
devicePath:
- description: devicePath is the path inside
- of the container that the device will
- be mapped to.
type: string
name:
- description: name must match the name
- of a persistentVolumeClaim in the pod
type: string
required:
- devicePath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
volumeMounts:
- description: Pod volumes to mount into the container's
- filesystem. Cannot be updated.
items:
- description: VolumeMount describes a mounting
- of a Volume within a container.
properties:
mountPath:
- description: Path within the container
- at which the volume should be mounted. Must
- not contain ':'.
type: string
mountPropagation:
- description: mountPropagation determines
- how mounts are propagated from the host
- to container and the other way around.
- When not set, MountPropagationNone is
- used. This field is beta in 1.10.
type: string
name:
- description: This must match the Name
- of a Volume.
type: string
readOnly:
- description: Mounted read-only if true,
- read-write otherwise (false or unspecified).
- Defaults to false.
type: boolean
+ recursiveReadOnly:
+ type: string
subPath:
- description: Path within the volume from
- which the container's volume should
- be mounted. Defaults to "" (volume's
- root).
type: string
subPathExpr:
- description: Expanded path within the
- volume from which the container's volume
- should be mounted. Behaves similarly
- to SubPath but environment variable
- references $(VAR_NAME) are expanded
- using the container's environment. Defaults
- to "" (volume's root). SubPathExpr and
- SubPath are mutually exclusive.
type: string
required:
- mountPath
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
workingDir:
- description: Container's working directory.
- If not specified, the container runtime's
- default will be used, which might be configured
- in the container image. Cannot be updated.
type: string
required:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
nodeName:
- description: NodeName is a request to schedule this
- pod onto a specific node. If it is non-empty, the
- scheduler simply schedules this pod onto that node,
- assuming that it fits resource requirements.
type: string
nodeSelector:
additionalProperties:
type: string
- description: 'NodeSelector is a selector which must
- be true for the pod to fit on a node. Selector which
- must match a node''s labels for the pod to be scheduled
- on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/'
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
type: object
overhead:
additionalProperties:
@@ -12027,472 +6890,247 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Overhead represents the resource overhead
- associated with running a pod for a given RuntimeClass.
- This field will be autopopulated at admission time
- by the RuntimeClass admission controller. If the
- RuntimeClass admission controller is enabled, overhead
- must not be set in Pod create requests. The RuntimeClass
- admission controller will reject Pod create requests
- which have the overhead already set. If RuntimeClass
- is configured and selected in the PodSpec, Overhead
- will be set to the value defined in the corresponding
- RuntimeClass, otherwise it will remain unset and
- treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md
- This field is alpha-level as of Kubernetes v1.16,
- and is only honored by servers that enable the PodOverhead
- feature.'
type: object
preemptionPolicy:
- description: PreemptionPolicy is the Policy for preempting
- pods with lower priority. One of Never, PreemptLowerPriority.
- Defaults to PreemptLowerPriority if unset. This
- field is beta-level, gated by the NonPreemptingPriority
- feature-gate.
type: string
priority:
- description: The priority value. Various system components
- use this field to find the priority of the pod.
- When Priority Admission Controller is enabled, it
- prevents users from setting this field. The admission
- controller populates this field from PriorityClassName.
- The higher the value, the higher the priority.
format: int32
type: integer
priorityClassName:
- description: If specified, indicates the pod's priority.
- "system-node-critical" and "system-cluster-critical"
- are two special keywords which indicate the highest
- priorities with the former being the highest priority.
- Any other name must be defined by creating a PriorityClass
- object with that name. If not specified, the pod
- priority will be default or zero if there is no
- default.
type: string
readinessGates:
- description: 'If specified, all readiness gates will
- be evaluated for pod readiness. A pod is ready when
- all its containers are ready AND all conditions
- specified in the readiness gates have status equal
- to "True" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md'
items:
- description: PodReadinessGate contains the reference
- to a pod condition
properties:
conditionType:
- description: ConditionType refers to a condition
- in the pod's condition list with matching
- type.
type: string
required:
- conditionType
type: object
type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
restartPolicy:
- description: 'Restart policy for all containers within
- the pod. One of Always, OnFailure, Never. Default
- to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy'
type: string
runtimeClassName:
- description: 'RuntimeClassName refers to a RuntimeClass
- object in the node.k8s.io group, which should be
- used to run this pod. If no RuntimeClass resource
- matches the named class, the pod will not be run.
- If unset or empty, the "legacy" RuntimeClass will
- be used, which is an implicit class with an empty
- definition that uses the default runtime handler.
- More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md
- This is a beta feature as of Kubernetes v1.14.'
type: string
schedulerName:
- description: If specified, the pod will be dispatched
- by specified scheduler. If not specified, the pod
- will be dispatched by default scheduler.
type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
securityContext:
- description: 'SecurityContext holds pod-level security
- attributes and common container settings. Optional:
- Defaults to empty. See type description for default
- values of each field.'
properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
fsGroup:
- description: "A special supplemental group that
- applies to all containers in a pod. Some volume
- types allow the Kubelet to change the ownership
- of that volume to be owned by the pod: \n 1.
- The owning GID will be the FSGroup 2. The setgid
- bit is set (new files created in the volume
- will be owned by FSGroup) 3. The permission
- bits are OR'd with rw-rw---- \n If unset, the
- Kubelet will not modify the ownership and permissions
- of any volume."
format: int64
type: integer
fsGroupChangePolicy:
- description: 'fsGroupChangePolicy defines behavior
- of changing ownership and permission of the
- volume before being exposed inside Pod. This
- field will only apply to volume types which
- support fsGroup based ownership(and permissions).
- It will have no effect on ephemeral volume types
- such as: secret, configmaps and emptydir. Valid
- values are "OnRootMismatch" and "Always". If
- not specified defaults to "Always".'
type: string
runAsGroup:
- description: The GID to run the entrypoint of
- the container process. Uses runtime default
- if unset. May also be set in SecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence for that container.
format: int64
type: integer
runAsNonRoot:
- description: Indicates that the container must
- run as a non-root user. If true, the Kubelet
- will validate the image at runtime to ensure
- that it does not run as UID 0 (root) and fail
- to start the container if it does. If unset
- or false, no such validation will be performed.
- May also be set in SecurityContext. If set
- in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence.
type: boolean
runAsUser:
- description: The UID to run the entrypoint of
- the container process. Defaults to user specified
- in image metadata if unspecified. May also be
- set in SecurityContext. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence for that
- container.
format: int64
type: integer
+ seLinuxChangePolicy:
+ type: string
seLinuxOptions:
- description: The SELinux context to be applied
- to all containers. If unspecified, the container
- runtime will allocate a random SELinux context
- for each container. May also be set in SecurityContext. If
- set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence for that container.
properties:
level:
- description: Level is SELinux level label
- that applies to the container.
type: string
role:
- description: Role is a SELinux role label
- that applies to the container.
type: string
type:
- description: Type is a SELinux type label
- that applies to the container.
type: string
user:
- description: User is a SELinux user label
- that applies to the container.
type: string
type: object
seccompProfile:
- description: The seccomp options to use by the
- containers in this pod.
properties:
localhostProfile:
- description: localhostProfile indicates a
- profile defined in a file on the node should
- be used. The profile must be preconfigured
- on the node to work. Must be a descending
- path, relative to the kubelet's configured
- seccomp profile location. Must only be set
- if type is "Localhost".
type: string
type:
- description: "type indicates which kind of
- seccomp profile will be applied. Valid options
- are: \n Localhost - a profile defined in
- a file on the node should be used. RuntimeDefault
- - the container runtime default profile
- should be used. Unconfined - no profile
- should be applied."
type: string
required:
- type
type: object
supplementalGroups:
- description: A list of groups applied to the first
- process run in each container, in addition to
- the container's primary GID. If unspecified,
- no groups will be added to any container.
items:
format: int64
type: integer
type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
sysctls:
- description: Sysctls hold a list of namespaced
- sysctls used for the pod. Pods with unsupported
- sysctls (by the container runtime) might fail
- to launch.
items:
- description: Sysctl defines a kernel parameter
- to be set
properties:
name:
- description: Name of a property to set
type: string
value:
- description: Value of a property to set
type: string
required:
- name
- value
type: object
type: array
+ x-kubernetes-list-type: atomic
windowsOptions:
- description: The Windows specific settings applied
- to all containers. If unspecified, the options
- within a container's SecurityContext will be
- used. If set in both SecurityContext and PodSecurityContext,
- the value specified in SecurityContext takes
- precedence.
properties:
gmsaCredentialSpec:
- description: GMSACredentialSpec is where the
- GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)
- inlines the contents of the GMSA credential
- spec named by the GMSACredentialSpecName
- field.
type: string
gmsaCredentialSpecName:
- description: GMSACredentialSpecName is the
- name of the GMSA credential spec to use.
type: string
+ hostProcess:
+ type: boolean
runAsUserName:
- description: The UserName in Windows to run
- the entrypoint of the container process.
- Defaults to the user specified in image
- metadata if unspecified. May also be set
- in PodSecurityContext. If set in both SecurityContext
- and PodSecurityContext, the value specified
- in SecurityContext takes precedence.
type: string
type: object
type: object
serviceAccount:
- description: 'DeprecatedServiceAccount is a depreciated
- alias for ServiceAccountName. Deprecated: Use serviceAccountName
- instead.'
type: string
serviceAccountName:
- description: 'ServiceAccountName is the name of the
- ServiceAccount to use to run this pod. More info:
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'
type: string
setHostnameAsFQDN:
- description: If true the pod's hostname will be configured
- as the pod's FQDN, rather than the leaf name (the
- default). In Linux containers, this means setting
- the FQDN in the hostname field of the kernel (the
- nodename field of struct utsname). In Windows containers,
- this means setting the registry value of hostname
- for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters
- to FQDN. If a pod does not have FQDN, this has no
- effect. Default to false.
type: boolean
shareProcessNamespace:
- description: 'Share a single process namespace between
- all of the containers in a pod. When this is set
- containers will be able to view and signal processes
- from other containers in the same pod, and the first
- process in each container will not be assigned PID
- 1. HostPID and ShareProcessNamespace cannot both
- be set. Optional: Default to false.'
type: boolean
subdomain:
- description: If specified, the fully qualified Pod
- hostname will be "...svc.". If not specified, the pod will not have
- a domainname at all.
type: string
terminationGracePeriodSeconds:
- description: Optional duration in seconds the pod
- needs to terminate gracefully. May be decreased
- in delete request. Value must be non-negative integer.
- The value zero indicates delete immediately. If
- this value is nil, the default grace period will
- be used instead. The grace period is the duration
- in seconds after the processes running in the pod
- are sent a termination signal and the time when
- the processes are forcibly halted with a kill signal.
- Set this value longer than the expected cleanup
- time for your process. Defaults to 30 seconds.
format: int64
type: integer
tolerations:
- description: If specified, the pod's tolerations.
items:
- description: The pod this Toleration is attached
- to tolerates any taint that matches the triple
- using the matching operator
- .
properties:
effect:
- description: Effect indicates the taint effect
- to match. Empty means match all taint effects.
- When specified, allowed values are NoSchedule,
- PreferNoSchedule and NoExecute.
type: string
key:
- description: Key is the taint key that the toleration
- applies to. Empty means match all taint keys.
- If the key is empty, operator must be Exists;
- this combination means to match all values
- and all keys.
type: string
operator:
- description: Operator represents a key's relationship
- to the value. Valid operators are Exists and
- Equal. Defaults to Equal. Exists is equivalent
- to wildcard for value, so that a pod can tolerate
- all taints of a particular category.
type: string
tolerationSeconds:
- description: TolerationSeconds represents the
- period of time the toleration (which must
- be of effect NoExecute, otherwise this field
- is ignored) tolerates the taint. By default,
- it is not set, which means tolerate the taint
- forever (do not evict). Zero and negative
- values will be treated as 0 (evict immediately)
- by the system.
format: int64
type: integer
value:
- description: Value is the taint value the toleration
- matches to. If the operator is Exists, the
- value should be empty, otherwise just a regular
- string.
type: string
type: object
type: array
+ x-kubernetes-list-type: atomic
topologySpreadConstraints:
- description: TopologySpreadConstraints describes how
- a group of pods ought to spread across topology
- domains. Scheduler will schedule pods in a way which
- abides by the constraints. All topologySpreadConstraints
- are ANDed.
items:
- description: TopologySpreadConstraint specifies
- how to spread matching pods among the given topology.
properties:
labelSelector:
- description: LabelSelector is used to find matching
- pods. Pods that match this label selector
- are counted to determine the number of pods
- in their corresponding topology domain.
properties:
matchExpressions:
- description: matchExpressions is a list
- of label selector requirements. The requirements
- are ANDed.
items:
- description: A label selector requirement
- is a selector that contains values,
- a key, and an operator that relates
- the key and values.
properties:
key:
- description: key is the label key
- that the selector applies to.
type: string
operator:
- description: operator represents a
- key's relationship to a set of values.
- Valid operators are In, NotIn, Exists
- and DoesNotExist.
type: string
values:
- description: values is an array of
- string values. If the operator is
- In or NotIn, the values array must
- be non-empty. If the operator is
- Exists or DoesNotExist, the values
- array must be empty. This array
- is replaced during a strategic merge
- patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is a map of {key,value}
- pairs. A single {key,value} in the matchLabels
- map is equivalent to an element of matchExpressions,
- whose key field is "key", the operator
- is "In", and the values array contains
- only "value". The requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
maxSkew:
- description: 'MaxSkew describes the degree to
- which pods may be unevenly distributed. When
- `whenUnsatisfiable=DoNotSchedule`, it is the
- maximum permitted difference between the number
- of matching pods in the target topology and
- the global minimum. For example, in a 3-zone
- cluster, MaxSkew is set to 1, and pods with
- the same labelSelector spread as 1/1/0: |
- zone1 | zone2 | zone3 | | P | P | |
- - if MaxSkew is 1, incoming pod can only be
- scheduled to zone3 to become 1/1/1; scheduling
- it onto zone1(zone2) would make the ActualSkew(2-0)
- on zone1(zone2) violate MaxSkew(1). - if MaxSkew
- is 2, incoming pod can be scheduled onto any
- zone. When `whenUnsatisfiable=ScheduleAnyway`,
- it is used to give higher precedence to topologies
- that satisfy it. It''s a required field. Default
- value is 1 and 0 is not allowed.'
format: int32
type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
topologyKey:
- description: TopologyKey is the key of node
- labels. Nodes that have a label with this
- key and identical values are considered to
- be in the same topology. We consider each
- as a "bucket", and try to put
- balanced number of pods into each bucket.
- It's a required field.
type: string
whenUnsatisfiable:
- description: 'WhenUnsatisfiable indicates how
- to deal with a pod if it doesn''t satisfy
- the spread constraint. - DoNotSchedule (default)
- tells the scheduler not to schedule it. -
- ScheduleAnyway tells the scheduler to schedule
- the pod in any location, but giving higher
- precedence to topologies that would help reduce
- the skew. A constraint is considered "Unsatisfiable"
- for an incoming pod if and only if every possible
- node assigment for that pod would violate
- "MaxSkew" on some topology. For example, in
- a 3-zone cluster, MaxSkew is set to 1, and
- pods with the same labelSelector spread as
- 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P |
- If WhenUnsatisfiable is set to DoNotSchedule,
- incoming pod can only be scheduled to zone2(zone3)
- to become 3/2/1(3/1/2) as ActualSkew(2-1)
- on zone2(zone3) satisfies MaxSkew(1). In other
- words, the cluster can still be imbalanced,
- but scheduler won''t make it *more* imbalanced.
- It''s a required field.'
type: string
required:
- maxSkew
@@ -12505,590 +7143,244 @@ spec:
- whenUnsatisfiable
x-kubernetes-list-type: map
volumes:
- description: 'List of volumes that can be mounted
- by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes'
items:
- description: Volume represents a named volume in
- a pod that may be accessed by any container in
- the pod.
properties:
awsElasticBlockStore:
- description: 'AWSElasticBlockStore represents
- an AWS Disk resource that is attached to a
- kubelet''s host machine and then exposed to
- the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
partition:
- description: 'The partition in the volume
- that you want to mount. If omitted, the
- default is to mount by volume name. Examples:
- For volume /dev/sda1, you specify the
- partition as "1". Similarly, the volume
- partition for /dev/sda is "0" (or you
- can leave the property empty).'
format: int32
type: integer
readOnly:
- description: 'Specify "true" to force and
- set the ReadOnly property in VolumeMounts
- to "true". If omitted, the default is
- "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
type: boolean
volumeID:
- description: 'Unique ID of the persistent
- disk resource in AWS (Amazon EBS volume).
- More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'
type: string
required:
- volumeID
type: object
azureDisk:
- description: AzureDisk represents an Azure Data
- Disk mount on the host and bind mount to the
- pod.
properties:
cachingMode:
- description: 'Host Caching mode: None, Read
- Only, Read Write.'
type: string
diskName:
- description: The Name of the data disk in
- the blob storage
type: string
diskURI:
- description: The URI the data disk in the
- blob storage
type: string
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
+ default: ext4
type: string
kind:
- description: 'Expected values Shared: multiple
- blob disks per storage account Dedicated:
- single blob disk per storage account Managed:
- azure managed data disk (only in managed
- availability set). defaults to shared'
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
+ default: false
type: boolean
required:
- diskName
- diskURI
type: object
azureFile:
- description: AzureFile represents an Azure File
- Service mount on the host and bind mount to
- the pod.
properties:
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretName:
- description: the name of secret that contains
- Azure Storage Account Name and Key
type: string
shareName:
- description: Share Name
type: string
required:
- secretName
- shareName
type: object
cephfs:
- description: CephFS represents a Ceph FS mount
- on the host that shares a pod's lifetime
properties:
monitors:
- description: 'Required: Monitors is a collection
- of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
path:
- description: 'Optional: Used as the mounted
- root, rather than the full Ceph tree,
- default is /'
type: string
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.
- More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: boolean
secretFile:
- description: 'Optional: SecretFile is the
- path to key ring for User, default is
- /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: string
secretRef:
- description: 'Optional: SecretRef is reference
- to the authentication secret for User,
- default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
user:
- description: 'Optional: User is the rados
- user name, default is admin More info:
- https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'
type: string
required:
- monitors
type: object
cinder:
- description: 'Cinder represents a cinder volume
- attached and mounted on kubelets host machine.
- More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
properties:
fsType:
- description: 'Filesystem type to mount.
- Must be a filesystem type supported by
- the host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: string
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.
- More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: boolean
secretRef:
- description: 'Optional: points to a secret
- object containing parameters used to connect
- to OpenStack.'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
volumeID:
- description: 'volume id used to identify
- the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'
type: string
required:
- volumeID
type: object
configMap:
- description: ConfigMap represents a configMap
- that should populate this volume
properties:
defaultMode:
- description: 'Optional: mode bits used to
- set permissions on created files by default.
- Must be an octal value between 0000 and
- 0777 or a decimal value between 0 and
- 511. YAML accepts both octal and decimal
- values, JSON requires decimal values for
- mode bits. Defaults to 0644. Directories
- within the path are not affected by this
- setting. This might be in conflict with
- other options that affect the file mode,
- like fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
items:
- description: If unspecified, each key-value
- pair in the Data field of the referenced
- ConfigMap will be projected into the volume
- as a file whose name is the key and content
- is the value. If specified, the listed
- keys will be projected into the specified
- paths, and unlisted keys will not be present.
- If a key is specified which is not present
- in the ConfigMap, the volume setup will
- error unless it is marked optional. Paths
- must be relative and may not contain the
- '..' path or start with '..'.
items:
- description: Maps a string key to a path
- within a volume.
properties:
key:
- description: The key to project.
type: string
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file. Must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: The relative path of
- the file to map the key to. May
- not be an absolute path. May not
- contain the path element '..'. May
- not start with the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent. More
- info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the ConfigMap
- or its keys must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
csi:
- description: CSI (Container Storage Interface)
- represents ephemeral storage that is handled
- by certain external CSI drivers (Beta feature).
properties:
driver:
- description: Driver is the name of the CSI
- driver that handles this volume. Consult
- with your admin for the correct name as
- registered in the cluster.
type: string
fsType:
- description: Filesystem type to mount. Ex.
- "ext4", "xfs", "ntfs". If not provided,
- the empty value is passed to the associated
- CSI driver which will determine the default
- filesystem to apply.
type: string
nodePublishSecretRef:
- description: NodePublishSecretRef is a reference
- to the secret object containing sensitive
- information to pass to the CSI driver
- to complete the CSI NodePublishVolume
- and NodeUnpublishVolume calls. This field
- is optional, and may be empty if no secret
- is required. If the secret object contains
- more than one secret, all secret references
- are passed.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
readOnly:
- description: Specifies a read-only configuration
- for the volume. Defaults to false (read/write).
type: boolean
volumeAttributes:
additionalProperties:
type: string
- description: VolumeAttributes stores driver-specific
- properties that are passed to the CSI
- driver. Consult your driver's documentation
- for supported values.
type: object
required:
- driver
type: object
downwardAPI:
- description: DownwardAPI represents downward
- API about the pod that should populate this
- volume
properties:
defaultMode:
- description: 'Optional: mode bits to use
- on created files by default. Must be a
- Optional: mode bits used to set permissions
- on created files by default. Must be an
- octal value between 0000 and 0777 or a
- decimal value between 0 and 511. YAML
- accepts both octal and decimal values,
- JSON requires decimal values for mode
- bits. Defaults to 0644. Directories within
- the path are not affected by this setting.
- This might be in conflict with other options
- that affect the file mode, like fsGroup,
- and the result can be other mode bits
- set.'
format: int32
type: integer
items:
- description: Items is a list of downward
- API volume file
items:
- description: DownwardAPIVolumeFile represents
- information to create the file containing
- the pod field
properties:
fieldRef:
- description: 'Required: Selects a
- field of the pod: only annotations,
- labels, name and namespace are supported.'
properties:
apiVersion:
- description: Version of the schema
- the FieldPath is written in
- terms of, defaults to "v1".
type: string
fieldPath:
- description: Path of the field
- to select in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file, must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: 'Required: Path is the
- relative path name of the file to
- be created. Must not be absolute
- or contain the ''..'' path. Must
- be utf-8 encoded. The first item
- of the relative path must not start
- with ''..'''
type: string
resourceFieldRef:
- description: 'Selects a resource of
- the container: only resources limits
- and requests (limits.cpu, limits.memory,
- requests.cpu and requests.memory)
- are currently supported.'
properties:
containerName:
- description: 'Container name:
- required for volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies the output
- format of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required: resource
- to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
required:
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
type: object
emptyDir:
- description: 'EmptyDir represents a temporary
- directory that shares a pod''s lifetime. More
- info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
properties:
medium:
- description: 'What type of storage medium
- should back this directory. The default
- is "" which means to use the node''s default
- medium. Must be an empty string (default)
- or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
type: string
sizeLimit:
anyOf:
- type: integer
- type: string
- description: 'Total amount of local storage
- required for this EmptyDir volume. The
- size limit is also applicable for memory
- medium. The maximum usage on memory medium
- EmptyDir would be the minimum value between
- the SizeLimit specified here and the sum
- of memory limits of all containers in
- a pod. The default is nil which means
- that the limit is undefined. More info:
- http://kubernetes.io/docs/user-guide/volumes#emptydir'
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
type: object
ephemeral:
- description: "Ephemeral represents a volume
- that is handled by a cluster storage driver
- (Alpha feature). The volume's lifecycle is
- tied to the pod that defines it - it will
- be created before the pod starts, and deleted
- when the pod is removed. \n Use this if: a)
- the volume is only needed while the pod runs,
- b) features of normal volumes like restoring
- from snapshot or capacity tracking are
- needed, c) the storage driver is specified
- through a storage class, and d) the storage
- driver supports dynamic volume provisioning
- through a PersistentVolumeClaim (see EphemeralVolumeSource
- for more information on the connection
- between this volume type and PersistentVolumeClaim).
- \n Use PersistentVolumeClaim or one of the
- vendor-specific APIs for volumes that persist
- for longer than the lifecycle of an individual
- pod. \n Use CSI for light-weight local ephemeral
- volumes if the CSI driver is meant to be used
- that way - see the documentation of the driver
- for more information. \n A pod can use both
- types of ephemeral volumes and persistent
- volumes at the same time."
properties:
- readOnly:
- description: Specifies a read-only configuration
- for the volume. Defaults to false (read/write).
- type: boolean
volumeClaimTemplate:
- description: "Will be used to create a stand-alone
- PVC to provision the volume. The pod in
- which this EphemeralVolumeSource is embedded
- will be the owner of the PVC, i.e. the
- PVC will be deleted together with the
- pod. The name of the PVC will be `-` where ``
- is the name from the `PodSpec.Volumes`
- array entry. Pod validation will reject
- the pod if the concatenated name is not
- valid for a PVC (for example, too long).
- \n An existing PVC with that name that
- is not owned by the pod will *not* be
- used for the pod to avoid using an unrelated
- volume by mistake. Starting the pod is
- then blocked until the unrelated PVC is
- removed. If such a pre-created PVC is
- meant to be used by the pod, the PVC has
- to updated with an owner reference to
- the pod once the pod exists. Normally
- this should not be necessary, but it may
- be useful when manually reconstructing
- a broken cluster. \n This field is read-only
- and no changes will be made by Kubernetes
- to the PVC after it has been created.
- \n Required, must not be nil."
properties:
metadata:
- description: May contain labels and
- annotations that will be copied into
- the PVC when creating it. No other
- fields are allowed and will be rejected
- during validation.
type: object
spec:
- description: The specification for the
- PersistentVolumeClaim. The entire
- content is copied unchanged into the
- PVC that gets created from this template.
- The same fields as in a PersistentVolumeClaim
- are also valid here.
properties:
accessModes:
- description: 'AccessModes contains
- the desired access modes the volume
- should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
dataSource:
- description: 'This field can be
- used to specify either: * An existing
- VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot
- - Beta) * An existing PVC (PersistentVolumeClaim)
- * An existing custom resource/object
- that implements data population
- (Alpha) In order to use VolumeSnapshot
- object types, the appropriate
- feature gate must be enabled (VolumeSnapshotDataSource
- or AnyVolumeDataSource) If the
- provisioner or an external controller
- can support the specified data
- source, it will create a new volume
- based on the contents of the specified
- data source. If the specified
- data source is not supported,
- the volume will not be created
- and the failure will be reported
- as an event. In the future, we
- plan to support more data source
- types and the behavior of the
- provisioner may change.'
properties:
apiGroup:
- description: APIGroup is the
- group for the resource being
- referenced. If APIGroup is
- not specified, the specified
- Kind must be in the core API
- group. For any other third-party
- types, APIGroup is required.
type: string
kind:
- description: Kind is the type
- of resource being referenced
type: string
name:
- description: Name is the name
- of resource being referenced
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
type: string
required:
- kind
- name
type: object
resources:
- description: 'Resources represents
- the minimum resources the volume
- should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'
properties:
limits:
additionalProperties:
@@ -13097,10 +7389,6 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes
- the maximum amount of compute
- resources allowed. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
requests:
additionalProperties:
@@ -13109,96 +7397,41 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes
- the minimum amount of compute
- resources required. If Requests
- is omitted for a container,
- it defaults to Limits if that
- is explicitly specified, otherwise
- to an implementation-defined
- value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'
type: object
type: object
selector:
- description: A label query over
- volumes to consider for binding.
properties:
matchExpressions:
- description: matchExpressions
- is a list of label selector
- requirements. The requirements
- are ANDed.
items:
- description: A label selector
- requirement is a selector
- that contains values, a
- key, and an operator that
- relates the key and values.
properties:
key:
- description: key is the
- label key that the selector
- applies to.
type: string
operator:
- description: operator
- represents a key's relationship
- to a set of values.
- Valid operators are
- In, NotIn, Exists and
- DoesNotExist.
type: string
values:
- description: values is
- an array of string values.
- If the operator is In
- or NotIn, the values
- array must be non-empty.
- If the operator is Exists
- or DoesNotExist, the
- values array must be
- empty. This array is
- replaced during a strategic
- merge patch.
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
- description: matchLabels is
- a map of {key,value} pairs.
- A single {key,value} in the
- matchLabels map is equivalent
- to an element of matchExpressions,
- whose key field is "key",
- the operator is "In", and
- the values array contains
- only "value". The requirements
- are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
storageClassName:
- description: 'Name of the StorageClass
- required by the claim. More info:
- https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'
+ type: string
+ volumeAttributesClassName:
type: string
volumeMode:
- description: volumeMode defines
- what type of volume is required
- by the claim. Value of Filesystem
- is implied when not included in
- claim spec.
type: string
volumeName:
- description: VolumeName is the binding
- reference to the PersistentVolume
- backing this claim.
type: string
type: object
required:
@@ -13206,293 +7439,140 @@ spec:
type: object
type: object
fc:
- description: FC represents a Fibre Channel resource
- that is attached to a kubelet's host machine
- and then exposed to the pod.
properties:
fsType:
- description: 'Filesystem type to mount.
- Must be a filesystem type supported by
- the host operating system. Ex. "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. TODO: how do
- we prevent errors in the filesystem from
- compromising the machine'
type: string
lun:
- description: 'Optional: FC target lun number'
format: int32
type: integer
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.'
type: boolean
targetWWNs:
- description: 'Optional: FC target worldwide
- names (WWNs)'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
wwids:
- description: 'Optional: FC volume world
- wide identifiers (wwids) Either wwids
- or combination of targetWWNs and lun must
- be set, but not both simultaneously.'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
type: object
flexVolume:
- description: FlexVolume represents a generic
- volume resource that is provisioned/attached
- using an exec based plugin.
properties:
driver:
- description: Driver is the name of the driver
- to use for this volume.
type: string
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". The default filesystem depends
- on FlexVolume script.
type: string
options:
additionalProperties:
type: string
- description: 'Optional: Extra command options
- if any.'
type: object
readOnly:
- description: 'Optional: Defaults to false
- (read/write). ReadOnly here will force
- the ReadOnly setting in VolumeMounts.'
type: boolean
secretRef:
- description: 'Optional: SecretRef is reference
- to the secret object containing sensitive
- information to pass to the plugin scripts.
- This may be empty if no secret object
- is specified. If the secret object contains
- more than one secret, all secrets are
- passed to the plugin scripts.'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
required:
- driver
type: object
flocker:
- description: Flocker represents a Flocker volume
- attached to a kubelet's host machine. This
- depends on the Flocker control service being
- running
properties:
datasetName:
- description: Name of the dataset stored
- as metadata -> name on the dataset for
- Flocker should be considered as deprecated
type: string
datasetUUID:
- description: UUID of the dataset. This is
- unique identifier of a Flocker dataset
type: string
type: object
gcePersistentDisk:
- description: 'GCEPersistentDisk represents a
- GCE Disk resource that is attached to a kubelet''s
- host machine and then exposed to the pod.
- More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
partition:
- description: 'The partition in the volume
- that you want to mount. If omitted, the
- default is to mount by volume name. Examples:
- For volume /dev/sda1, you specify the
- partition as "1". Similarly, the volume
- partition for /dev/sda is "0" (or you
- can leave the property empty). More info:
- https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
format: int32
type: integer
pdName:
- description: 'Unique name of the PD resource
- in GCE. Used to identify the disk in GCE.
- More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
type: string
readOnly:
- description: 'ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'
type: boolean
required:
- pdName
type: object
gitRepo:
- description: 'GitRepo represents a git repository
- at a particular revision. DEPRECATED: GitRepo
- is deprecated. To provision a container with
- a git repo, mount an EmptyDir into an InitContainer
- that clones the repo using git, then mount
- the EmptyDir into the Pod''s container.'
properties:
directory:
- description: Target directory name. Must
- not contain or start with '..'. If '.'
- is supplied, the volume directory will
- be the git repository. Otherwise, if
- specified, the volume will contain the
- git repository in the subdirectory with
- the given name.
type: string
repository:
- description: Repository URL
type: string
revision:
- description: Commit hash for the specified
- revision.
type: string
required:
- repository
type: object
glusterfs:
- description: 'Glusterfs represents a Glusterfs
- mount on the host that shares a pod''s lifetime.
- More info: https://examples.k8s.io/volumes/glusterfs/README.md'
properties:
endpoints:
- description: 'EndpointsName is the endpoint
- name that details Glusterfs topology.
- More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: string
path:
- description: 'Path is the Glusterfs volume
- path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: string
readOnly:
- description: 'ReadOnly here will force the
- Glusterfs volume to be mounted with read-only
- permissions. Defaults to false. More info:
- https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'
type: boolean
required:
- endpoints
- path
type: object
hostPath:
- description: 'HostPath represents a pre-existing
- file or directory on the host machine that
- is directly exposed to the container. This
- is generally used for system agents or other
- privileged things that are allowed to see
- the host machine. Most containers will NOT
- need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
- --- TODO(jonesdl) We need to restrict who
- can use host directory mounts and who can/can
- not mount host directories as read/write.'
properties:
path:
- description: 'Path of the directory on the
- host. If the path is a symlink, it will
- follow the link to the real path. More
- info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
type: string
type:
- description: 'Type for HostPath Volume Defaults
- to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'
type: string
required:
- path
type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
iscsi:
- description: 'ISCSI represents an ISCSI Disk
- resource that is attached to a kubelet''s
- host machine and then exposed to the pod.
- More info: https://examples.k8s.io/volumes/iscsi/README.md'
properties:
chapAuthDiscovery:
- description: whether support iSCSI Discovery
- CHAP authentication
type: boolean
chapAuthSession:
- description: whether support iSCSI Session
- CHAP authentication
type: boolean
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
initiatorName:
- description: Custom iSCSI Initiator Name.
- If initiatorName is specified with iscsiInterface
- simultaneously, new iSCSI interface : will be created
- for the connection.
type: string
iqn:
- description: Target iSCSI Qualified Name.
type: string
iscsiInterface:
- description: iSCSI Interface Name that uses
- an iSCSI transport. Defaults to 'default'
- (tcp).
+ default: default
type: string
lun:
- description: iSCSI Target Lun number.
format: int32
type: integer
portals:
- description: iSCSI Target Portal List. The
- portal is either an IP or ip_addr:port
- if the port is other than default (typically
- TCP ports 860 and 3260).
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
readOnly:
- description: ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false.
type: boolean
secretRef:
- description: CHAP Secret for iSCSI target
- and initiator authentication
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
targetPortal:
- description: iSCSI Target Portal. The Portal
- is either an IP or ip_addr:port if the
- port is other than default (typically
- TCP ports 860 and 3260).
type: string
required:
- iqn
@@ -13500,577 +7580,301 @@ spec:
- targetPortal
type: object
name:
- description: 'Volume''s name. Must be a DNS_LABEL
- and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
nfs:
- description: 'NFS represents an NFS mount on
- the host that shares a pod''s lifetime More
- info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
properties:
path:
- description: 'Path that is exported by the
- NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: string
readOnly:
- description: 'ReadOnly here will force the
- NFS export to be mounted with read-only
- permissions. Defaults to false. More info:
- https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: boolean
server:
- description: 'Server is the hostname or
- IP address of the NFS server. More info:
- https://kubernetes.io/docs/concepts/storage/volumes#nfs'
type: string
required:
- path
- server
type: object
persistentVolumeClaim:
- description: 'PersistentVolumeClaimVolumeSource
- represents a reference to a PersistentVolumeClaim
- in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
properties:
claimName:
- description: 'ClaimName is the name of a
- PersistentVolumeClaim in the same namespace
- as the pod using this volume. More info:
- https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'
type: string
readOnly:
- description: Will force the ReadOnly setting
- in VolumeMounts. Default false.
type: boolean
required:
- claimName
type: object
photonPersistentDisk:
- description: PhotonPersistentDisk represents
- a PhotonController persistent disk attached
- and mounted on kubelets host machine
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
pdID:
- description: ID that identifies Photon Controller
- persistent disk
type: string
required:
- pdID
type: object
portworxVolume:
- description: PortworxVolume represents a portworx
- volume attached and mounted on kubelets host
- machine
properties:
fsType:
- description: FSType represents the filesystem
- type to mount Must be a filesystem type
- supported by the host operating system.
- Ex. "ext4", "xfs". Implicitly inferred
- to be "ext4" if unspecified.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
volumeID:
- description: VolumeID uniquely identifies
- a Portworx volume
type: string
required:
- volumeID
type: object
projected:
- description: Items for all in one resources
- secrets, configmaps, and downward API
properties:
defaultMode:
- description: Mode bits used to set permissions
- on created files by default. Must be an
- octal value between 0000 and 0777 or a
- decimal value between 0 and 511. YAML
- accepts both octal and decimal values,
- JSON requires decimal values for mode
- bits. Directories within the path are
- not affected by this setting. This might
- be in conflict with other options that
- affect the file mode, like fsGroup, and
- the result can be other mode bits set.
format: int32
type: integer
sources:
- description: list of volume projections
items:
- description: Projection that may be projected
- along with other supported volume types
properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
configMap:
- description: information about the
- configMap data to project
properties:
items:
- description: If unspecified, each
- key-value pair in the Data field
- of the referenced ConfigMap
- will be projected into the volume
- as a file whose name is the
- key and content is the value.
- If specified, the listed keys
- will be projected into the specified
- paths, and unlisted keys will
- not be present. If a key is
- specified which is not present
- in the ConfigMap, the volume
- setup will error unless it is
- marked optional. Paths must
- be relative and may not contain
- the '..' path or start with
- '..'.
items:
- description: Maps a string key
- to a path within a volume.
properties:
key:
- description: The key to
- project.
type: string
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file.
- Must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: The relative
- path of the file to map
- the key to. May not be
- an absolute path. May
- not contain the path element
- '..'. May not start with
- the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- ConfigMap or its keys must be
- defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
downwardAPI:
- description: information about the
- downwardAPI data to project
properties:
items:
- description: Items is a list of
- DownwardAPIVolume file
items:
- description: DownwardAPIVolumeFile
- represents information to
- create the file containing
- the pod field
properties:
fieldRef:
- description: 'Required:
- Selects a field of the
- pod: only annotations,
- labels, name and namespace
- are supported.'
properties:
apiVersion:
- description: Version
- of the schema the
- FieldPath is written
- in terms of, defaults
- to "v1".
type: string
fieldPath:
- description: Path of
- the field to select
- in the specified API
- version.
type: string
required:
- fieldPath
type: object
+ x-kubernetes-map-type: atomic
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file,
- must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: 'Required:
- Path is the relative
- path name of the file
- to be created. Must not
- be absolute or contain
- the ''..'' path. Must
- be utf-8 encoded. The
- first item of the relative
- path must not start with
- ''..'''
type: string
resourceFieldRef:
- description: 'Selects a
- resource of the container:
- only resources limits
- and requests (limits.cpu,
- limits.memory, requests.cpu
- and requests.memory) are
- currently supported.'
properties:
containerName:
- description: 'Container
- name: required for
- volumes, optional
- for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
- description: Specifies
- the output format
- of the exposed resources,
- defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
- description: 'Required:
- resource to select'
type: string
required:
- resource
type: object
+ x-kubernetes-map-type: atomic
required:
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
type: object
secret:
- description: information about the
- secret data to project
properties:
items:
- description: If unspecified, each
- key-value pair in the Data field
- of the referenced Secret will
- be projected into the volume
- as a file whose name is the
- key and content is the value.
- If specified, the listed keys
- will be projected into the specified
- paths, and unlisted keys will
- not be present. If a key is
- specified which is not present
- in the Secret, the volume setup
- will error unless it is marked
- optional. Paths must be relative
- and may not contain the '..'
- path or start with '..'.
items:
- description: Maps a string key
- to a path within a volume.
properties:
key:
- description: The key to
- project.
type: string
mode:
- description: 'Optional:
- mode bits used to set
- permissions on this file.
- Must be an octal value
- between 0000 and 0777
- or a decimal value between
- 0 and 511. YAML accepts
- both octal and decimal
- values, JSON requires
- decimal values for mode
- bits. If not specified,
- the volume defaultMode
- will be used. This might
- be in conflict with other
- options that affect the
- file mode, like fsGroup,
- and the result can be
- other mode bits set.'
format: int32
type: integer
path:
- description: The relative
- path of the file to map
- the key to. May not be
- an absolute path. May
- not contain the path element
- '..'. May not start with
- the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields.
- apiVersion, kind, uid?'
+ default: ""
type: string
optional:
- description: Specify whether the
- Secret or its key must be defined
type: boolean
type: object
+ x-kubernetes-map-type: atomic
serviceAccountToken:
- description: information about the
- serviceAccountToken data to project
properties:
audience:
- description: Audience is the intended
- audience of the token. A recipient
- of a token must identify itself
- with an identifier specified
- in the audience of the token,
- and otherwise should reject
- the token. The audience defaults
- to the identifier of the apiserver.
type: string
expirationSeconds:
- description: ExpirationSeconds
- is the requested duration of
- validity of the service account
- token. As the token approaches
- expiration, the kubelet volume
- plugin will proactively rotate
- the service account token. The
- kubelet will start trying to
- rotate the token if the token
- is older than 80 percent of
- its time to live or if the token
- is older than 24 hours.Defaults
- to 1 hour and must be at least
- 10 minutes.
format: int64
type: integer
path:
- description: Path is the path
- relative to the mount point
- of the file to project the token
- into.
type: string
required:
- path
type: object
type: object
type: array
- required:
- - sources
+ x-kubernetes-list-type: atomic
type: object
quobyte:
- description: Quobyte represents a Quobyte mount
- on the host that shares a pod's lifetime
properties:
group:
- description: Group to map volume access
- to Default is no group
type: string
readOnly:
- description: ReadOnly here will force the
- Quobyte volume to be mounted with read-only
- permissions. Defaults to false.
type: boolean
registry:
- description: Registry represents a single
- or multiple Quobyte Registry services
- specified as a string as host:port pair
- (multiple entries are separated with commas)
- which acts as the central registry for
- volumes
type: string
tenant:
- description: Tenant owning the given Quobyte
- volume in the Backend Used with dynamically
- provisioned Quobyte volumes, value is
- set by the plugin
type: string
user:
- description: User to map volume access to
- Defaults to serivceaccount user
type: string
volume:
- description: Volume is a string that references
- an already created Quobyte volume by name.
type: string
required:
- registry
- volume
type: object
rbd:
- description: 'RBD represents a Rados Block Device
- mount on the host that shares a pod''s lifetime.
- More info: https://examples.k8s.io/volumes/rbd/README.md'
properties:
fsType:
- description: 'Filesystem type of the volume
- that you want to mount. Tip: Ensure that
- the filesystem type is supported by the
- host operating system. Examples: "ext4",
- "xfs", "ntfs". Implicitly inferred to
- be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd
- TODO: how do we prevent errors in the
- filesystem from compromising the machine'
type: string
image:
- description: 'The rados image name. More
- info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
type: string
keyring:
- description: 'Keyring is the path to key
- ring for RBDUser. Default is /etc/ceph/keyring.
- More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: /etc/ceph/keyring
type: string
monitors:
- description: 'A collection of Ceph monitors.
- More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
pool:
- description: 'The rados pool name. Default
- is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: rbd
type: string
readOnly:
- description: 'ReadOnly here will force the
- ReadOnly setting in VolumeMounts. Defaults
- to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
type: boolean
secretRef:
- description: 'SecretRef is name of the authentication
- secret for RBDUser. If provided overrides
- keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
user:
- description: 'The rados user name. Default
- is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'
+ default: admin
type: string
required:
- image
- monitors
type: object
scaleIO:
- description: ScaleIO represents a ScaleIO persistent
- volume attached and mounted on Kubernetes
- nodes.
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Default is "xfs".
+ default: xfs
type: string
gateway:
- description: The host address of the ScaleIO
- API Gateway.
type: string
protectionDomain:
- description: The name of the ScaleIO Protection
- Domain for the configured storage.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretRef:
- description: SecretRef references to the
- secret for ScaleIO user and other sensitive
- information. If this is not provided,
- Login operation will fail.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
sslEnabled:
- description: Flag to enable/disable SSL
- communication with Gateway, default false
type: boolean
storageMode:
- description: Indicates whether the storage
- for a volume should be ThickProvisioned
- or ThinProvisioned. Default is ThinProvisioned.
+ default: ThinProvisioned
type: string
storagePool:
- description: The ScaleIO Storage Pool associated
- with the protection domain.
type: string
system:
- description: The name of the storage system
- as configured in ScaleIO.
type: string
volumeName:
- description: The name of a volume already
- created in the ScaleIO system that is
- associated with this volume source.
type: string
required:
- gateway
@@ -14078,154 +7882,58 @@ spec:
- system
type: object
secret:
- description: 'Secret represents a secret that
- should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
properties:
defaultMode:
- description: 'Optional: mode bits used to
- set permissions on created files by default.
- Must be an octal value between 0000 and
- 0777 or a decimal value between 0 and
- 511. YAML accepts both octal and decimal
- values, JSON requires decimal values for
- mode bits. Defaults to 0644. Directories
- within the path are not affected by this
- setting. This might be in conflict with
- other options that affect the file mode,
- like fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
items:
- description: If unspecified, each key-value
- pair in the Data field of the referenced
- Secret will be projected into the volume
- as a file whose name is the key and content
- is the value. If specified, the listed
- keys will be projected into the specified
- paths, and unlisted keys will not be present.
- If a key is specified which is not present
- in the Secret, the volume setup will error
- unless it is marked optional. Paths must
- be relative and may not contain the '..'
- path or start with '..'.
items:
- description: Maps a string key to a path
- within a volume.
properties:
key:
- description: The key to project.
type: string
mode:
- description: 'Optional: mode bits
- used to set permissions on this
- file. Must be an octal value between
- 0000 and 0777 or a decimal value
- between 0 and 511. YAML accepts
- both octal and decimal values, JSON
- requires decimal values for mode
- bits. If not specified, the volume
- defaultMode will be used. This might
- be in conflict with other options
- that affect the file mode, like
- fsGroup, and the result can be other
- mode bits set.'
format: int32
type: integer
path:
- description: The relative path of
- the file to map the key to. May
- not be an absolute path. May not
- contain the path element '..'. May
- not start with the string '..'.
type: string
required:
- key
- path
type: object
type: array
+ x-kubernetes-list-type: atomic
optional:
- description: Specify whether the Secret
- or its keys must be defined
type: boolean
secretName:
- description: 'Name of the secret in the
- pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret'
type: string
type: object
storageos:
- description: StorageOS represents a StorageOS
- volume attached and mounted on Kubernetes
- nodes.
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
readOnly:
- description: Defaults to false (read/write).
- ReadOnly here will force the ReadOnly
- setting in VolumeMounts.
type: boolean
secretRef:
- description: SecretRef specifies the secret
- to use for obtaining the StorageOS API
- credentials. If not specified, default
- values will be attempted.
properties:
name:
- description: 'Name of the referent.
- More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion,
- kind, uid?'
+ default: ""
type: string
type: object
+ x-kubernetes-map-type: atomic
volumeName:
- description: VolumeName is the human-readable
- name of the StorageOS volume. Volume
- names are only unique within a namespace.
type: string
volumeNamespace:
- description: VolumeNamespace specifies the
- scope of the volume within StorageOS. If
- no namespace is specified then the Pod's
- namespace will be used. This allows the
- Kubernetes name scoping to be mirrored
- within StorageOS for tighter integration.
- Set VolumeName to any name to override
- the default behaviour. Set to "default"
- if you are not using namespaces within
- StorageOS. Namespaces that do not pre-exist
- within StorageOS will be created.
type: string
type: object
vsphereVolume:
- description: VsphereVolume represents a vSphere
- volume attached and mounted on kubelets host
- machine
properties:
fsType:
- description: Filesystem type to mount. Must
- be a filesystem type supported by the
- host operating system. Ex. "ext4", "xfs",
- "ntfs". Implicitly inferred to be "ext4"
- if unspecified.
type: string
storagePolicyID:
- description: Storage Policy Based Management
- (SPBM) profile ID associated with the
- StoragePolicyName.
type: string
storagePolicyName:
- description: Storage Policy Based Management
- (SPBM) profile name.
type: string
volumePath:
- description: Path that identifies vSphere
- volume vmdk
type: string
required:
- volumePath
@@ -14234,21 +7942,14 @@ spec:
- name
type: object
type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
required:
- containers
type: object
type: object
ttlSecondsAfterFinished:
- description: ttlSecondsAfterFinished limits the lifetime of
- a Job that has finished execution (either Complete or Failed).
- If this field is set, ttlSecondsAfterFinished after the
- Job finishes, it is eligible to be automatically deleted.
- When the Job is being deleted, its lifecycle guarantees
- (e.g. finalizers) will be honored. If this field is unset,
- the Job won't be automatically deleted. If this field is
- set to zero, the Job becomes eligible to be deleted immediately
- after it finishes. This field is alpha-level and is only
- honored by servers that enable the TTLAfterFinished feature.
format: int32
type: integer
required:
@@ -14256,128 +7957,104 @@ spec:
type: object
type: object
schedule:
- description: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
properties:
dayOfMonth:
- description: specifies the day of the month during which the job
- executes.
type: string
dayOfWeek:
- description: specifies the day of the week during which the job
- executes.
type: string
hour:
- description: specifies the hour during which the job executes.
type: string
minute:
- description: specifies the minute during which the job executes.
type: string
month:
- description: specifies the month during which the job executes.
type: string
type: object
startingDeadlineSeconds:
- description: Optional deadline in seconds for starting the job if
- it misses scheduled time for any reason. Missed jobs executions
- will be counted as failed ones.
format: int64
minimum: 0
type: integer
successfulJobsHistoryLimit:
- description: The number of successful finished jobs to retain. This
- is a pointer to distinguish between explicit zero and not specified.
format: int32
minimum: 0
type: integer
suspend:
- description: This flag tells the controller to suspend subsequent
- executions, it does not apply to already started executions. Defaults
- to false.
type: boolean
required:
- jobTemplate
- schedule
type: object
status:
- description: CronJobStatus defines the observed state of CronJob
properties:
active:
- description: A list of pointers to currently running jobs.
items:
- description: 'ObjectReference contains enough information to let
- you inspect or modify the referred object. --- New uses of this
- type are discouraged because of difficulty describing its usage
- when embedded in APIs. 1. Ignored fields. It includes many fields
- which are not generally honored. For instance, ResourceVersion
- and FieldPath are both very rarely valid in actual usage. 2.
- Invalid usage help. It is impossible to add specific help for
- individual usage. In most embedded usages, there are particular restrictions
- like, "must refer only to types A and B" or "UID not honored"
- or "name must be restricted". Those cannot be well described
- when embedded. 3. Inconsistent validation. Because the usages
- are different, the validation rules are different by usage, which
- makes it hard for users to predict what will happen. 4. The fields
- are both imprecise and overly precise. Kind is not a precise
- mapping to a URL. This can produce ambiguity during interpretation
- and require a REST mapping. In most cases, the dependency is
- on the group,resource tuple and the version of the actual
- struct is irrelevant. 5. We cannot easily change it. Because
- this type is embedded in many locations, updates to this type will
- affect numerous schemas. Don''t make new APIs embed an underspecified
- API type they do not control. Instead of using this type, create
- a locally provided and used type that is well-focused on your
- reference. For example, ServiceReferences for admission registration:
- https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533
- .'
properties:
apiVersion:
- description: API version of the referent.
type: string
fieldPath:
- description: 'If referring to a piece of an object instead of
- an entire object, this string should contain a valid JSON/Go
- field access statement, such as desiredState.manifest.containers[2].
- For example, if the object reference is to a container within
- a pod, this would take on a value like: "spec.containers{name}"
- (where "name" refers to the name of the container that triggered
- the event) or if no container name is specified "spec.containers[2]"
- (container with index 2 in this pod). This syntax is chosen
- only to have some well-defined way of referencing a part of
- an object. TODO: this design is not final and this field is
- subject to change in the future.'
type: string
kind:
- description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
name:
- description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
type: string
namespace:
- description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
type: string
resourceVersion:
- description: 'Specific resourceVersion to which this reference
- is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
type: string
uid:
- description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
type: string
type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
lastScheduleTime:
- description: Information when was the last time the job was successfully
- scheduled.
format: date-time
type: string
type: object
+ required:
+ - spec
type: object
served: true
storage: false
subresources:
status: {}
-status:
- acceptedNames:
- kind: ""
- plural: ""
- conditions: []
- storedVersions: []
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 bcc0a355f28..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
@@ -3,19 +3,15 @@
# It should be run by config/default
resources:
- bases/batch.tutorial.kubebuilder.io_cronjobs.yaml
-#+kubebuilder:scaffold:crdkustomizeresource
+# +kubebuilder:scaffold:crdkustomizeresource
-patchesStrategicMerge:
+patches:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
-#- patches/webhook_in_cronjobs.yaml
-#+kubebuilder:scaffold:crdkustomizewebhookpatch
-
-# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
-# patches here are for enabling the CA injection for each CRD
-#- patches/cainjection_in_cronjobs.yaml
-#+kubebuilder:scaffold:crdkustomizecainjectionpatch
+- path: patches/webhook_in_cronjobs.yaml
+# +kubebuilder:scaffold:crdkustomizewebhookpatch
+# [WEBHOOK] To enable webhook, uncomment the following section
# the following config is for teaching kustomize how to do kustomization for CRDs.
configurations:
- kustomizeconfig.yaml
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 7b037c0e41f..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/crd/patches/webhook_in_cronjobs.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
index 76d1d9a3744..3e96e59513a 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/crd/patches/webhook_in_cronjobs.yaml
@@ -12,3 +12,5 @@ spec:
namespace: system
name: webhook-service
path: /convert
+ conversionReviewVersions:
+ - v1
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 5261fc02d46..0cc2ff2331b 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
@@ -9,10 +9,12 @@ namespace: project-system
namePrefix: project-
# Labels to add to all resources and selectors.
-#commonLabels:
-# someName: someValue
+#labels:
+#- includeSelectors: true
+# pairs:
+# someName: someValue
-bases:
+resources:
- ../crd
- ../rbac
- ../manager
@@ -22,53 +24,229 @@ bases:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
-#- ../prometheus
+- ../prometheus
+# [METRICS] Expose the controller manager metrics service.
+- metrics_service.yaml
+# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
+# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
+# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will
+# be able to communicate with the Webhook Server.
+#- ../network-policy
-patchesStrategicMerge:
-# Protect the /metrics endpoint by putting it behind auth.
-# If you want your controller-manager to expose the /metrics
-# endpoint w/o any authn/z, please comment the following line.
-- manager_auth_proxy_patch.yaml
+# 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
+- path: manager_metrics_patch.yaml
+ target:
+ kind: Deployment
-# Mount the controller config file for loading manager configurations
-# through a ComponentConfig type
-#- manager_config_patch.yaml
+# 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
-- manager_webhook_patch.yaml
+- path: manager_webhook_patch.yaml
+ target:
+ kind: Deployment
-# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
-# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
-# 'CERTMANAGER' needs to be enabled to use ca injection
-#- webhookcainjection_patch.yaml
-
-# the following config is for teaching kustomize how to do var substitution
-vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
-- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
- objref:
- kind: Certificate
- group: cert-manager.io
- version: v1
- name: serving-cert # this name should match the one in certificate.yaml
- fieldref:
- fieldpath: metadata.namespace
-- name: CERTIFICATE_NAME
- objref:
- kind: Certificate
- group: cert-manager.io
- version: v1
- name: serving-cert # this name should match the one in certificate.yaml
-- name: SERVICE_NAMESPACE # namespace of the service
- objref:
- kind: Service
- version: v1
- name: webhook-service
- fieldref:
- fieldpath: metadata.namespace
-- name: SERVICE_NAME
- objref:
- kind: Service
- version: v1
- name: webhook-service
+# 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
+ - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor
+ kind: ServiceMonitor
+ group: monitoring.coreos.com
+ version: v1
+ name: controller-manager-metrics-monitor
+ fieldPaths:
+ - spec.endpoints.0.tlsConfig.serverName
+ 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
+ - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor
+ kind: ServiceMonitor
+ group: monitoring.coreos.com
+ version: v1
+ name: controller-manager-metrics-monitor
+ fieldPaths:
+ - spec.endpoints.0.tlsConfig.serverName
+ options:
+ delimiter: '.'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have any webhook
+ kind: Service
+ version: v1
+ name: webhook-service
+ fieldPath: .metadata.name # Name of the service
+ targets:
+ - select:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPaths:
+ - .spec.dnsNames.0
+ - .spec.dnsNames.1
+ options:
+ delimiter: '.'
+ index: 0
+ create: true
+ - source:
+ kind: Service
+ version: v1
+ name: webhook-service
+ fieldPath: .metadata.namespace # Namespace of the service
+ targets:
+ - select:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPaths:
+ - .spec.dnsNames.0
+ - .spec.dnsNames.1
+ options:
+ delimiter: '.'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation)
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert # This name should match the one in certificate.yaml
+ fieldPath: .metadata.namespace # Namespace of the certificate CR
+ targets:
+ - select:
+ kind: ValidatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 0
+ create: true
+ - source:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.name
+ targets:
+ - select:
+ kind: ValidatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting )
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.namespace # Namespace of the certificate CR
+ targets:
+ - select:
+ kind: MutatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 0
+ create: true
+ - source:
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.name
+ targets:
+ - select:
+ kind: MutatingWebhookConfiguration
+ fieldPaths:
+ - .metadata.annotations.[cert-manager.io/inject-ca-from]
+ options:
+ delimiter: '/'
+ index: 1
+ create: true
+
+ - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
+ kind: Certificate
+ group: cert-manager.io
+ version: v1
+ name: serving-cert
+ fieldPath: .metadata.namespace # Namespace of the certificate CR
+ 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
+ fieldPath: .metadata.name
+ 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_auth_proxy_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml
deleted file mode 100644
index a224be19ea1..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_auth_proxy_patch.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-# This patch inject a sidecar container which is a HTTP proxy for the
-# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-spec:
- template:
- spec:
- containers:
- - name: kube-rbac-proxy
- image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
- args:
- - "--secure-listen-address=0.0.0.0:8443"
- - "--upstream=http://127.0.0.1:8080/"
- - "--logtostderr=true"
- - "--v=10"
- ports:
- - containerPort: 8443
- name: https
- - name: manager
- args:
- - "--health-probe-bind-address=:8081"
- - "--metrics-bind-address=127.0.0.1:8080"
- - "--leader-elect"
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_config_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_config_patch.yaml
deleted file mode 100644
index 6c400155cfb..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_config_patch.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-spec:
- template:
- spec:
- containers:
- - name: manager
- args:
- - "--config=controller_manager_config.yaml"
- volumeMounts:
- - name: manager-config
- mountPath: /controller_manager_config.yaml
- subPath: controller_manager_config.yaml
- volumes:
- - name: manager-config
- configMap:
- name: manager-config
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_metrics_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_metrics_patch.yaml
new file mode 100644
index 00000000000..2aaef6536f4
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/default/manager_metrics_patch.yaml
@@ -0,0 +1,4 @@
+# This patch adds the args to allow exposing the metrics endpoint using HTTPS
+- op: add
+ path: /spec/template/spec/containers/0/args/0
+ value: --metrics-bind-address=:8443
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 738de350b71..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,23 +1,31 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: controller-manager
- namespace: system
-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
new file mode 100644
index 00000000000..df85a7387c5
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/default/metrics_service.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: controller-manager-metrics-service
+ namespace: system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: 8443
+ selector:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml
deleted file mode 100644
index 02ab515d428..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/default/webhookcainjection_patch.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# This patch add annotation to admission webhook config and
-# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
-apiVersion: admissionregistration.k8s.io/v1
-kind: MutatingWebhookConfiguration
-metadata:
- name: mutating-webhook-configuration
- annotations:
- cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
----
-apiVersion: admissionregistration.k8s.io/v1
-kind: ValidatingWebhookConfiguration
-metadata:
- name: validating-webhook-configuration
- annotations:
- cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/manager b/docs/book/src/multiversion-tutorial/testdata/project/config/manager
deleted file mode 120000
index e32a157bb42..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/manager
+++ /dev/null
@@ -1 +0,0 @@
-../../../../cronjob-tutorial/testdata/project/config/manager
\ No newline at end of file
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/manager/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/manager/kustomization.yaml
new file mode 100644
index 00000000000..ad13e96b3fc
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/manager/kustomization.yaml
@@ -0,0 +1,8 @@
+resources:
+- manager.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+images:
+- name: controller
+ newName: controller
+ newTag: latest
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
new file mode 100644
index 00000000000..8fb2249a918
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/manager/manager.yaml
@@ -0,0 +1,99 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: system
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: controller-manager
+ namespace: system
+ labels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+spec:
+ selector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ replicas: 1
+ template:
+ metadata:
+ annotations:
+ 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.
+ # It is considered best practice to support multiple architectures. You can
+ # build your manager image using the makefile target docker-buildx.
+ # affinity:
+ # nodeAffinity:
+ # requiredDuringSchedulingIgnoredDuringExecution:
+ # nodeSelectorTerms:
+ # - matchExpressions:
+ # - key: kubernetes.io/arch
+ # operator: In
+ # values:
+ # - amd64
+ # - arm64
+ # - ppc64le
+ # - s390x
+ # - key: kubernetes.io/os
+ # operator: In
+ # 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
+ seccompProfile:
+ type: RuntimeDefault
+ containers:
+ - command:
+ - /manager
+ args:
+ - --leader-elect
+ - --health-probe-bind-address=:8081
+ image: controller:latest
+ name: manager
+ ports: []
+ securityContext:
+ readOnlyRootFilesystem: true
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - "ALL"
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ # TODO(user): Configure the resources accordingly based on the project requirements.
+ # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ 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
new file mode 100644
index 00000000000..0e64d74ef6e
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-metrics-traffic.yaml
@@ -0,0 +1,27 @@
+# This NetworkPolicy allows ingress traffic
+# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
+# namespaces are able to gather data from the metrics endpoint.
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: allow-metrics-traffic
+ namespace: system
+spec:
+ podSelector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ policyTypes:
+ - Ingress
+ ingress:
+ # This allows ingress traffic from any namespace with the label metrics: enabled
+ - from:
+ - namespaceSelector:
+ matchLabels:
+ metrics: enabled # Only from namespaces with this label
+ ports:
+ - port: 8443
+ protocol: TCP
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
new file mode 100644
index 00000000000..eb9464b64ea
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/allow-webhook-traffic.yaml
@@ -0,0 +1,27 @@
+# This NetworkPolicy allows ingress traffic to your webhook server running
+# as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks
+# will only work when applied in namespaces labeled with 'webhook: enabled'
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: allow-webhook-traffic
+ namespace: system
+spec:
+ podSelector:
+ matchLabels:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
+ policyTypes:
+ - Ingress
+ ingress:
+ # This allows ingress traffic from any namespace with the label webhook: enabled
+ - from:
+ - namespaceSelector:
+ matchLabels:
+ webhook: enabled # Only from namespaces with this label
+ ports:
+ - port: 443
+ protocol: TCP
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/kustomization.yaml
new file mode 100644
index 00000000000..0872bee124c
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/network-policy/kustomization.yaml
@@ -0,0 +1,3 @@
+resources:
+- allow-webhook-traffic.yaml
+- allow-metrics-traffic.yaml
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 d19136ae710..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
@@ -1,20 +1,27 @@
-
# Prometheus Monitor Service (Metrics)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
control-plane: controller-manager
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: controller-manager-metrics-monitor
namespace: system
spec:
endpoints:
- path: /metrics
- port: https
+ port: https # Ensure this is the name of the port that exposes HTTPS metrics
scheme: https
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, 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..5bf84ce0d53
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/prometheus/monitor_tls_patch.yaml
@@ -0,0 +1,19 @@
+# Patch for Prometheus ServiceMonitor to enable secure TLS configuration
+# using certificates managed by cert-manager
+- op: replace
+ path: /spec/endpoints/0/tlsConfig
+ value:
+ # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
+ serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
+ 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 b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac
deleted file mode 120000
index ee6d2fccb28..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac
+++ /dev/null
@@ -1 +0,0 @@
-../../../../cronjob-tutorial/testdata/project/config/rbac
\ No newline at end of file
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
new file mode 100644
index 00000000000..f0ccbbe8662
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_editor_role.yaml
@@ -0,0 +1,33 @@
+# 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:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: cronjob-editor-role
+rules:
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
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
new file mode 100644
index 00000000000..d8200790df3
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/cronjob_viewer_role.yaml
@@ -0,0 +1,29 @@
+# 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:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: cronjob-viewer-role
+rules:
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
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
new file mode 100644
index 00000000000..097aba38485
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/kustomization.yaml
@@ -0,0 +1,28 @@
+resources:
+# All RBAC will be applied under this service account in
+# the deployment namespace. You may comment out this resource
+# if your manager will use a service account that exists at
+# runtime. Be sure to update RoleBinding and ClusterRoleBinding
+# subjects if changing service account names.
+- service_account.yaml
+- role.yaml
+- role_binding.yaml
+- leader_election_role.yaml
+- leader_election_role_binding.yaml
+# The following RBAC configurations are used to protect
+# the metrics endpoint with authn/authz. These configurations
+# ensure that only authorized users and service accounts
+# can access the metrics endpoint. Comment the following
+# permissions if you want to disable this protection.
+# More info: https://book.kubebuilder.io/reference/metrics.html
+- metrics_auth_role.yaml
+- metrics_auth_role_binding.yaml
+- metrics_reader_role.yaml
+# 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
+# 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/rbac/leader_election_role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/leader_election_role.yaml
new file mode 100644
index 00000000000..e3fc403c0d9
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/leader_election_role.yaml
@@ -0,0 +1,40 @@
+# permissions to do leader election.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: leader-election-role
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
new file mode 100644
index 00000000000..133026ff212
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/leader_election_role_binding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: leader-election-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: leader-election-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_auth_role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_auth_role.yaml
new file mode 100644
index 00000000000..32d2e4ec6b0
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_auth_role.yaml
@@ -0,0 +1,17 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: metrics-auth-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_auth_role_binding.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_auth_role_binding.yaml
new file mode 100644
index 00000000000..e775d67ff08
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_auth_role_binding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: metrics-auth-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
diff --git a/testdata/project-v3-multigroup/config/rbac/auth_proxy_client_clusterrole.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_reader_role.yaml
similarity index 100%
rename from testdata/project-v3-multigroup/config/rbac/auth_proxy_client_clusterrole.yaml
rename to docs/book/src/multiversion-tutorial/testdata/project/config/rbac/metrics_reader_role.yaml
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/role.yaml
new file mode 100644
index 00000000000..8f87d954c79
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/role.yaml
@@ -0,0 +1,50 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: manager-role
+rules:
+- apiGroups:
+ - batch
+ resources:
+ - jobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch
+ resources:
+ - jobs/status
+ verbs:
+ - get
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+ - patch
+ - update
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/role_binding.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/role_binding.yaml
new file mode 100644
index 00000000000..1e81c2443c8
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/role_binding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: manager-role
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/service_account.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/service_account.yaml
new file mode 100644
index 00000000000..7733e7fc663
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/rbac/service_account.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: controller-manager
+ namespace: system
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
index 12d70ccf68f..dbf5df5e8d9 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v1_cronjob.yaml
@@ -1,6 +1,9 @@
apiVersion: batch.tutorial.kubebuilder.io/v1
kind: CronJob
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: cronjob-sample
spec:
schedule: "*/1 * * * *"
@@ -10,6 +13,11 @@ spec:
spec:
template:
spec:
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ seccompProfile:
+ type: RuntimeDefault
containers:
- name: hello
image: busybox
@@ -17,4 +25,11 @@ spec:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: false
restartPolicy: OnFailure
+
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml
index 103d39bae1e..88c3ba8f8a5 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/samples/batch_v2_cronjob.yaml
@@ -1,6 +1,9 @@
apiVersion: batch.tutorial.kubebuilder.io/v2
kind: CronJob
metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
name: cronjob-sample
spec:
schedule:
@@ -11,6 +14,11 @@ spec:
spec:
template:
spec:
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ seccompProfile:
+ type: RuntimeDefault
containers:
- name: hello
image: busybox
@@ -18,4 +26,11 @@ spec:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: false
restartPolicy: OnFailure
+
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/samples/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/samples/kustomization.yaml
new file mode 100644
index 00000000000..0ac8e91e40b
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/samples/kustomization.yaml
@@ -0,0 +1,5 @@
+## Append samples of your project ##
+resources:
+- batch_v1_cronjob.yaml
+- batch_v2_cronjob.yaml
+# +kubebuilder:scaffold:manifestskustomizesamples
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/webhook b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook
deleted file mode 120000
index 1e18b38d081..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/config/webhook
+++ /dev/null
@@ -1 +0,0 @@
-../../../../cronjob-tutorial/testdata/project/config/webhook/
\ No newline at end of file
diff --git a/docs/book/src/component-config-tutorial/testdata/project/config/webhook/kustomization.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/kustomization.yaml
similarity index 100%
rename from docs/book/src/component-config-tutorial/testdata/project/config/webhook/kustomization.yaml
rename to docs/book/src/multiversion-tutorial/testdata/project/config/webhook/kustomization.yaml
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
new file mode 100644
index 00000000000..206316e54ff
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/kustomizeconfig.yaml
@@ -0,0 +1,22 @@
+# the following config is for teaching kustomize where to look at when substituting nameReference.
+# It requires kustomize v2.1.0 or newer to work properly.
+nameReference:
+- kind: Service
+ version: v1
+ fieldSpecs:
+ - kind: MutatingWebhookConfiguration
+ group: admissionregistration.k8s.io
+ path: webhooks/clientConfig/service/name
+ - kind: ValidatingWebhookConfiguration
+ group: admissionregistration.k8s.io
+ path: webhooks/clientConfig/service/name
+
+namespace:
+- kind: MutatingWebhookConfiguration
+ group: admissionregistration.k8s.io
+ path: webhooks/clientConfig/service/namespace
+ create: true
+- kind: ValidatingWebhookConfiguration
+ group: admissionregistration.k8s.io
+ path: webhooks/clientConfig/service/namespace
+ create: true
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/manifests.yaml b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/manifests.yaml
new file mode 100644
index 00000000000..801980c73c4
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/manifests.yaml
@@ -0,0 +1,92 @@
+---
+apiVersion: admissionregistration.k8s.io/v1
+kind: MutatingWebhookConfiguration
+metadata:
+ name: mutating-webhook-configuration
+webhooks:
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: system
+ path: /mutate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: system
+ path: /mutate-batch-tutorial-kubebuilder-io-v2-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v2.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+---
+apiVersion: admissionregistration.k8s.io/v1
+kind: ValidatingWebhookConfiguration
+metadata:
+ name: validating-webhook-configuration
+webhooks:
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: system
+ path: /validate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: webhook-service
+ namespace: system
+ path: /validate-batch-tutorial-kubebuilder-io-v2-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v2.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
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
new file mode 100644
index 00000000000..34c7252616d
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/config/webhook/service.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/name: project
+ app.kubernetes.io/managed-by: kustomize
+ name: webhook-service
+ namespace: system
+spec:
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: 9443
+ selector:
+ control-plane: controller-manager
+ app.kubernetes.io/name: project
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/controllers/cronjob_controller.go b/docs/book/src/multiversion-tutorial/testdata/project/controllers/cronjob_controller.go
deleted file mode 100644
index b37f13fbbaf..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/controllers/cronjob_controller.go
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-/*
-We'll start out with some imports. You'll see below that we'll need a few more imports
-than those scaffolded for us. We'll talk about each one when we use it.
-*/
-package controllers
-
-import (
- "context"
- "fmt"
- "sort"
- "time"
-
- "github.com/go-logr/logr"
- "github.com/robfig/cron"
- kbatch "k8s.io/api/batch/v1"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- ref "k8s.io/client-go/tools/reference"
- ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/client"
-
- batch "tutorial.kubebuilder.io/project/api/v1"
-)
-
-/*
-Next, we'll need a Clock, which will allow us to fake timing in our tests.
-*/
-
-// CronJobReconciler reconciles a CronJob object
-type CronJobReconciler struct {
- client.Client
- Log logr.Logger
- Scheme *runtime.Scheme
- Clock
-}
-
-/*
-We'll mock out the clock to make it easier to jump around in time while testing,
-the "real" clock just calls `time.Now`.
-*/
-type realClock struct{}
-
-func (_ realClock) Now() time.Time { return time.Now() }
-
-// clock knows how to get the current time.
-// It can be used to fake out timing for testing.
-type Clock interface {
- Now() time.Time
-}
-
-// +kubebuilder:docs-gen:collapse=Clock
-
-/*
-Notice that we need a few more RBAC permissions -- since we're creating and
-managing jobs now, we'll need permissions for those, which means adding
-a couple more [markers](/reference/markers/rbac.md).
-*/
-
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
-//+kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
-//+kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
-//+kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=get
-
-/*
-Now, we get to the heart of the controller -- the reconciler logic.
-*/
-var (
- scheduledTimeAnnotation = "batch.tutorial.kubebuilder.io/scheduled-at"
-)
-
-func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
- log := r.Log.WithValues("cronjob", req.NamespacedName)
-
- /*
- ### 1: Load the CronJob by name
-
- We'll fetch the CronJob using our client. All client methods take a
- context (to allow for cancellation) as their first argument, and the object
- in question as their last. Get is a bit special, in that it takes a
- [`NamespacedName`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client?tab=doc#ObjectKey)
- as the middle argument (most don't have a middle argument, as we'll see
- below).
-
- Many client methods also take variadic options at the end.
- */
- var cronJob batch.CronJob
- if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
- log.Error(err, "unable to fetch CronJob")
- // we'll ignore not-found errors, since they can't be fixed by an immediate
- // requeue (we'll need to wait for a new notification), and we can get them
- // on deleted requests.
- return ctrl.Result{}, client.IgnoreNotFound(err)
- }
-
- /*
- ### 2: List all active jobs, and update the status
-
- To fully update our status, we'll need to list all child jobs in this namespace that belong to this CronJob.
- Similarly to Get, we can use the List method to list the child jobs. Notice that we use variadic options to
- set the namespace and field match (which is actually an index lookup that we set up below).
- */
- var childJobs kbatch.JobList
- if err := r.List(ctx, &childJobs, client.InNamespace(req.Namespace), client.MatchingFields{jobOwnerKey: req.Name}); err != nil {
- log.Error(err, "unable to list child Jobs")
- return ctrl.Result{}, err
- }
-
- /*
-
-
-
- What is this index about?
-
- The reconciler fetches all jobs owned by the cronjob for the status. As our number of cronjobs increases,
- looking these up can become quite slow as we have to filter through all of them. For a more efficient lookup,
- these jobs will be indexed locally on the controller's name. A jobOwnerKey field is added to the
- cached job objects. This key references the owning controller and functions as the index. Later in this
- document we will configure the manager to actually index this field.
-
-
-
- Once we have all the jobs we own, we'll split them into active, successful,
- and failed jobs, keeping track of the most recent run so that we can record it
- in status. Remember, status should be able to be reconstituted from the state
- of the world, so it's generally not a good idea to read from the status of the
- root object. Instead, you should reconstruct it every run. That's what we'll
- do here.
-
- We can check if a job is "finished" and whether it succeeded or failed using status
- conditions. We'll put that logic in a helper to make our code cleaner.
- */
-
- // find the active list of jobs
- var activeJobs []*kbatch.Job
- var successfulJobs []*kbatch.Job
- var failedJobs []*kbatch.Job
- var mostRecentTime *time.Time // find the last run so we can update the status
-
- /*
- We consider a job "finished" if it has a "Complete" or "Failed" condition marked as true.
- Status conditions allow us to add extensible status information to our objects that other
- humans and controllers can examine to check things like completion and health.
- */
- isJobFinished := func(job *kbatch.Job) (bool, kbatch.JobConditionType) {
- for _, c := range job.Status.Conditions {
- if (c.Type == kbatch.JobComplete || c.Type == kbatch.JobFailed) && c.Status == corev1.ConditionTrue {
- return true, c.Type
- }
- }
-
- return false, ""
- }
- // +kubebuilder:docs-gen:collapse=isJobFinished
-
- /*
- We'll use a helper to extract the scheduled time from the annotation that
- we added during job creation.
- */
- getScheduledTimeForJob := func(job *kbatch.Job) (*time.Time, error) {
- timeRaw := job.Annotations[scheduledTimeAnnotation]
- if len(timeRaw) == 0 {
- return nil, nil
- }
-
- timeParsed, err := time.Parse(time.RFC3339, timeRaw)
- if err != nil {
- return nil, err
- }
- return &timeParsed, nil
- }
- // +kubebuilder:docs-gen:collapse=getScheduledTimeForJob
-
- for i, job := range childJobs.Items {
- _, finishedType := isJobFinished(&job)
- switch finishedType {
- case "": // ongoing
- activeJobs = append(activeJobs, &childJobs.Items[i])
- case kbatch.JobFailed:
- failedJobs = append(failedJobs, &childJobs.Items[i])
- case kbatch.JobComplete:
- successfulJobs = append(successfulJobs, &childJobs.Items[i])
- }
-
- // We'll store the launch time in an annotation, so we'll reconstitute that from
- // the active jobs themselves.
- scheduledTimeForJob, err := getScheduledTimeForJob(&job)
- if err != nil {
- log.Error(err, "unable to parse schedule time for child job", "job", &job)
- continue
- }
- if scheduledTimeForJob != nil {
- if mostRecentTime == nil {
- mostRecentTime = scheduledTimeForJob
- } else if mostRecentTime.Before(*scheduledTimeForJob) {
- mostRecentTime = scheduledTimeForJob
- }
- }
- }
-
- if mostRecentTime != nil {
- cronJob.Status.LastScheduleTime = &metav1.Time{Time: *mostRecentTime}
- } else {
- cronJob.Status.LastScheduleTime = nil
- }
- cronJob.Status.Active = nil
- for _, activeJob := range activeJobs {
- jobRef, err := ref.GetReference(r.Scheme, activeJob)
- if err != nil {
- log.Error(err, "unable to make reference to active job", "job", activeJob)
- continue
- }
- cronJob.Status.Active = append(cronJob.Status.Active, *jobRef)
- }
-
- /*
- Here, we'll log how many jobs we observed at a slightly higher logging level,
- for debugging. Notice how instead of using a format string, we use a fixed message,
- and attach key-value pairs with the extra information. This makes it easier to
- filter and query log lines.
- */
- log.V(1).Info("job count", "active jobs", len(activeJobs), "successful jobs", len(successfulJobs), "failed jobs", len(failedJobs))
-
- /*
- Using the date we've gathered, we'll update the status of our CRD.
- Just like before, we use our client. To specifically update the status
- subresource, we'll use the `Status` part of the client, with the `Update`
- method.
-
- The status subresource ignores changes to spec, so it's less likely to conflict
- with any other updates, and can have separate permissions.
- */
- if err := r.Status().Update(ctx, &cronJob); err != nil {
- log.Error(err, "unable to update CronJob status")
- return ctrl.Result{}, err
- }
-
- /*
- Once we've updated our status, we can move on to ensuring that the status of
- the world matches what we want in our spec.
-
- ### 3: Clean up old jobs according to the history limit
-
- First, we'll try to clean up old jobs, so that we don't leave too many lying
- around.
- */
-
- // NB: deleting these is "best effort" -- if we fail on a particular one,
- // we won't requeue just to finish the deleting.
- if cronJob.Spec.FailedJobsHistoryLimit != nil {
- sort.Slice(failedJobs, func(i, j int) bool {
- if failedJobs[i].Status.StartTime == nil {
- return failedJobs[j].Status.StartTime != nil
- }
- return failedJobs[i].Status.StartTime.Before(failedJobs[j].Status.StartTime)
- })
- for i, job := range failedJobs {
- if int32(i) >= int32(len(failedJobs))-*cronJob.Spec.FailedJobsHistoryLimit {
- break
- }
- if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
- log.Error(err, "unable to delete old failed job", "job", job)
- } else {
- log.V(0).Info("deleted old failed job", "job", job)
- }
- }
- }
-
- if cronJob.Spec.SuccessfulJobsHistoryLimit != nil {
- sort.Slice(successfulJobs, func(i, j int) bool {
- if successfulJobs[i].Status.StartTime == nil {
- return successfulJobs[j].Status.StartTime != nil
- }
- return successfulJobs[i].Status.StartTime.Before(successfulJobs[j].Status.StartTime)
- })
- for i, job := range successfulJobs {
- if int32(i) >= int32(len(successfulJobs))-*cronJob.Spec.SuccessfulJobsHistoryLimit {
- break
- }
- if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); (err) != nil {
- log.Error(err, "unable to delete old successful job", "job", job)
- } else {
- log.V(0).Info("deleted old successful job", "job", job)
- }
- }
- }
-
- /* ### 4: Check if we're suspended
-
- If this object is suspended, we don't want to run any jobs, so we'll stop now.
- This is useful if something's broken with the job we're running and we want to
- pause runs to investigate or putz with the cluster, without deleting the object.
- */
-
- if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend {
- log.V(1).Info("cronjob suspended, skipping")
- return ctrl.Result{}, nil
- }
-
- /*
- ### 5: Get the next scheduled run
-
- If we're not paused, we'll need to calculate the next scheduled run, and whether
- or not we've got a run that we haven't processed yet.
- */
-
- /*
- We'll calculate the next scheduled time using our helpful cron library.
- We'll start calculating appropriate times from our last run, or the creation
- of the CronJob if we can't find a last run.
-
- If there are too many missed runs and we don't have any deadlines set, we'll
- bail so that we don't cause issues on controller restarts or wedges.
-
- Otherwise, we'll just return the missed runs (of which we'll just use the latest),
- and the next run, so that we can know when it's time to reconcile again.
- */
- getNextSchedule := func(cronJob *batch.CronJob, now time.Time) (lastMissed time.Time, next time.Time, err error) {
- sched, err := cron.ParseStandard(cronJob.Spec.Schedule)
- if err != nil {
- return time.Time{}, time.Time{}, fmt.Errorf("Unparseable schedule %q: %v", cronJob.Spec.Schedule, err)
- }
-
- // for optimization purposes, cheat a bit and start from our last observed run time
- // we could reconstitute this here, but there's not much point, since we've
- // just updated it.
- var earliestTime time.Time
- if cronJob.Status.LastScheduleTime != nil {
- earliestTime = cronJob.Status.LastScheduleTime.Time
- } else {
- earliestTime = cronJob.ObjectMeta.CreationTimestamp.Time
- }
- if cronJob.Spec.StartingDeadlineSeconds != nil {
- // controller is not going to schedule anything below this point
- schedulingDeadline := now.Add(-time.Second * time.Duration(*cronJob.Spec.StartingDeadlineSeconds))
-
- if schedulingDeadline.After(earliestTime) {
- earliestTime = schedulingDeadline
- }
- }
- if earliestTime.After(now) {
- return time.Time{}, sched.Next(now), nil
- }
-
- starts := 0
- for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) {
- lastMissed = t
- // An object might miss several starts. For example, if
- // controller gets wedged on Friday at 5:01pm when everyone has
- // gone home, and someone comes in on Tuesday AM and discovers
- // the problem and restarts the controller, then all the hourly
- // jobs, more than 80 of them for one hourly scheduledJob, should
- // all start running with no further intervention (if the scheduledJob
- // allows concurrency and late starts).
- //
- // However, if there is a bug somewhere, or incorrect clock
- // on controller's server or apiservers (for setting creationTimestamp)
- // then there could be so many missed start times (it could be off
- // by decades or more), that it would eat up all the CPU and memory
- // of this controller. In that case, we want to not try to list
- // all the missed start times.
- starts++
- if starts > 100 {
- // We can't get the most recent times so just return an empty slice
- return time.Time{}, time.Time{}, fmt.Errorf("Too many missed start times (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.")
- }
- }
- return lastMissed, sched.Next(now), nil
- }
- // +kubebuilder:docs-gen:collapse=getNextSchedule
-
- // figure out the next times that we need to create
- // jobs at (or anything we missed).
- missedRun, nextRun, err := getNextSchedule(&cronJob, r.Now())
- if err != nil {
- log.Error(err, "unable to figure out CronJob schedule")
- // we don't really care about requeuing until we get an update that
- // fixes the schedule, so don't return an error
- return ctrl.Result{}, nil
- }
-
- /*
- We'll prep our eventual request to requeue until the next job, and then figure
- out if we actually need to run.
- */
- scheduledResult := ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())} // save this so we can re-use it elsewhere
- log = log.WithValues("now", r.Now(), "next run", nextRun)
-
- /*
- ### 6: Run a new job if it's on schedule, not past the deadline, and not blocked by our concurrency policy
-
- If we've missed a run, and we're still within the deadline to start it, we'll need to run a job.
- */
- if missedRun.IsZero() {
- log.V(1).Info("no upcoming scheduled times, sleeping until next")
- return scheduledResult, nil
- }
-
- // make sure we're not too late to start the run
- log = log.WithValues("current run", missedRun)
- tooLate := false
- if cronJob.Spec.StartingDeadlineSeconds != nil {
- tooLate = missedRun.Add(time.Duration(*cronJob.Spec.StartingDeadlineSeconds) * time.Second).Before(r.Now())
- }
- if tooLate {
- log.V(1).Info("missed starting deadline for last run, sleeping till next")
- // TODO(directxman12): events
- return scheduledResult, nil
- }
-
- /*
- If we actually have to run a job, we'll need to either wait till existing ones finish,
- replace the existing ones, or just add new ones. If our information is out of date due
- to cache delay, we'll get a requeue when we get up-to-date information.
- */
- // figure out how to run this job -- concurrency policy might forbid us from running
- // multiple at the same time...
- if cronJob.Spec.ConcurrencyPolicy == batch.ForbidConcurrent && len(activeJobs) > 0 {
- log.V(1).Info("concurrency policy blocks concurrent runs, skipping", "num active", len(activeJobs))
- return scheduledResult, nil
- }
-
- // ...or instruct us to replace existing ones...
- if cronJob.Spec.ConcurrencyPolicy == batch.ReplaceConcurrent {
- for _, activeJob := range activeJobs {
- // we don't care if the job was already deleted
- if err := r.Delete(ctx, activeJob, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
- log.Error(err, "unable to delete active job", "job", activeJob)
- return ctrl.Result{}, err
- }
- }
- }
-
- /*
- Once we've figured out what to do with existing jobs, we'll actually create our desired job
- */
-
- /*
- We need to construct a job based on our CronJob's template. We'll copy over the spec
- from the template and copy some basic object meta.
-
- Then, we'll set the "scheduled time" annotation so that we can reconstitute our
- `LastScheduleTime` field each reconcile.
-
- Finally, we'll need to set an owner reference. This allows the Kubernetes garbage collector
- to clean up jobs when we delete the CronJob, and allows controller-runtime to figure out
- which cronjob needs to be reconciled when a given job changes (is added, deleted, completes, etc).
- */
- constructJobForCronJob := func(cronJob *batch.CronJob, scheduledTime time.Time) (*kbatch.Job, error) {
- // We want job names for a given nominal start time to have a deterministic name to avoid the same job being created twice
- name := fmt.Sprintf("%s-%d", cronJob.Name, scheduledTime.Unix())
-
- job := &kbatch.Job{
- ObjectMeta: metav1.ObjectMeta{
- Labels: make(map[string]string),
- Annotations: make(map[string]string),
- Name: name,
- Namespace: cronJob.Namespace,
- },
- Spec: *cronJob.Spec.JobTemplate.Spec.DeepCopy(),
- }
- for k, v := range cronJob.Spec.JobTemplate.Annotations {
- job.Annotations[k] = v
- }
- job.Annotations[scheduledTimeAnnotation] = scheduledTime.Format(time.RFC3339)
- for k, v := range cronJob.Spec.JobTemplate.Labels {
- job.Labels[k] = v
- }
- if err := ctrl.SetControllerReference(cronJob, job, r.Scheme); err != nil {
- return nil, err
- }
-
- return job, nil
- }
- // +kubebuilder:docs-gen:collapse=constructJobForCronJob
-
- // actually make the job...
- job, err := constructJobForCronJob(&cronJob, missedRun)
- if err != nil {
- log.Error(err, "unable to construct job from template")
- // don't bother requeuing until we get a change to the spec
- return scheduledResult, nil
- }
-
- // ...and create it on the cluster
- if err := r.Create(ctx, job); err != nil {
- log.Error(err, "unable to create Job for CronJob", "job", job)
- return ctrl.Result{}, err
- }
-
- log.V(1).Info("created Job for CronJob run", "job", job)
-
- /*
- ### 7: Requeue when we either see a running job or it's time for the next scheduled run
-
- Finally, we'll return the result that we prepped above, that says we want to requeue
- when our next run would need to occur. This is taken as a maximum deadline -- if something
- else changes in between, like our job starts or finishes, we get modified, etc, we might
- reconcile again sooner.
- */
- // we'll requeue once we see the running job, and update our status
- return scheduledResult, nil
-}
-
-/*
-### Setup
-
-Finally, we'll update our setup. In order to allow our reconciler to quickly
-look up Jobs by their owner, we'll need an index. We declare an index key that
-we can later use with the client as a pseudo-field name, and then describe how to
-extract the indexed value from the Job object. The indexer will automatically take
-care of namespaces for us, so we just have to extract the owner name if the Job has
-a CronJob owner.
-
-Additionally, we'll inform the manager that this controller owns some Jobs, so that it
-will automatically call Reconcile on the underlying CronJob when a Job changes, is
-deleted, etc.
-*/
-var (
- jobOwnerKey = ".metadata.controller"
- apiGVStr = batch.GroupVersion.String()
-)
-
-func (r *CronJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
- // set up a real clock, since we're not in a test
- if r.Clock == nil {
- r.Clock = realClock{}
- }
-
- if err := mgr.GetFieldIndexer().IndexField(context.Background(), &kbatch.Job{}, jobOwnerKey, func(rawObj client.Object) []string {
- // grab the job object, extract the owner...
- job := rawObj.(*kbatch.Job)
- owner := metav1.GetControllerOf(job)
- if owner == nil {
- return nil
- }
- // ...make sure it's a CronJob...
- if owner.APIVersion != apiGVStr || owner.Kind != "CronJob" {
- return nil
- }
-
- // ...and if so, return it
- return []string{owner.Name}
- }); err != nil {
- return err
- }
-
- return ctrl.NewControllerManagedBy(mgr).
- For(&batch.CronJob{}).
- Owns(&kbatch.Job{}).
- Complete(r)
-}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/controllers/suite_test.go b/docs/book/src/multiversion-tutorial/testdata/project/controllers/suite_test.go
deleted file mode 100644
index 3ca6dd53c52..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/controllers/suite_test.go
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package controllers
-
-import (
- "path/filepath"
- "testing"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- "k8s.io/client-go/kubernetes/scheme"
- "k8s.io/client-go/rest"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/envtest"
- "sigs.k8s.io/controller-runtime/pkg/envtest/printer"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-
- 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 cfg *rest.Config
-var k8sClient client.Client
-var testEnv *envtest.Environment
-
-func TestAPIs(t *testing.T) {
- RegisterFailHandler(Fail)
-
- RunSpecsWithDefaultAndCustomReporters(t,
- "Controller Suite",
- []Reporter{printer.NewlineReporter{}})
-}
-
-var _ = BeforeSuite(func() {
- logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
- By("bootstrapping test environment")
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
- ErrorIfCRDPathMissing: true,
- }
-
- cfg, err := testEnv.Start()
- Expect(err).NotTo(HaveOccurred())
- Expect(cfg).NotTo(BeNil())
-
- err = batchv1.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())
-
-}, 60)
-
-var _ = AfterSuite(func() {
- By("tearing down the test environment")
- err := testEnv.Stop()
- Expect(err).NotTo(HaveOccurred())
-})
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/.helmignore b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/.helmignore
new file mode 100644
index 00000000000..7d92f7fb4f1
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/.helmignore
@@ -0,0 +1,25 @@
+# Patterns to ignore when building Helm packages.
+# Operating system files
+.DS_Store
+
+# Version control directories
+.git/
+.gitignore
+.bzr/
+.hg/
+.hgignore
+.svn/
+
+# Backup and temporary files
+*.swp
+*.tmp
+*.bak
+*.orig
+*~
+
+# IDE and editor-related files
+.idea/
+.vscode/
+
+# Helm chart artifacts
+dist/chart/*.tgz
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/Chart.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/Chart.yaml
new file mode 100644
index 00000000000..4781b2de9dd
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/Chart.yaml
@@ -0,0 +1,7 @@
+apiVersion: v2
+name: project
+description: A Helm chart to distribute the project project
+type: application
+version: 0.1.0
+appVersion: "0.1.0"
+icon: "https://example.com/icon.png"
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/_helpers.tpl b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/_helpers.tpl
new file mode 100644
index 00000000000..5aced50daec
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/_helpers.tpl
@@ -0,0 +1,78 @@
+{{/*
+Chart name based on project name.
+Truncated to 63 characters for Kubernetes compatibility.
+*/}}
+{{- define "chart.name" -}}
+{{- if .Chart }}
+ {{- if .Chart.Name }}
+ {{- .Chart.Name | trunc 63 | trimSuffix "-" }}
+ {{- else }}
+ project
+ {{- end }}
+{{- else }}
+ project
+{{- end }}
+{{- end }}
+
+{{/*
+Full name of the chart (with release name prefix).
+Combines release name with chart name.
+Truncated to 63 characters for Kubernetes compatibility.
+*/}}
+{{- define "chart.fullname" -}}
+{{- $name := include "chart.name" . }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+
+{{/*
+Namespace for generated references.
+Always uses the Helm release namespace.
+*/}}
+{{- define "chart.namespaceName" -}}
+{{ .Release.Namespace }}
+{{- end }}
+
+
+
+{{/*
+Service name with proper truncation for Kubernetes 63-character limit.
+Takes a context with .suffix for the service type (e.g., "webhook-service").
+If fullname + suffix exceeds 63 chars, truncates fullname to 45 chars.
+*/}}
+{{- define "chart.serviceName" -}}
+{{- $fullname := include "chart.fullname" .context -}}
+{{- if gt (len $fullname) 45 -}}
+{{- printf "%s-%s" (trunc 45 $fullname | trimSuffix "-") .suffix | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" $fullname .suffix | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end }}
+
+{{/*
+Common labels for Helm charts.
+Includes app version, chart version, app name, instance, and managed-by labels.
+*/}}
+{{- define "chart.labels" -}}
+{{- if .Chart.AppVersion -}}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+{{- if .Chart.Version }}
+helm.sh/chart: {{ .Chart.Version | quote }}
+{{- end }}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels for matching pods and services.
+Only includes name and instance for consistent selection.
+*/}}
+{{- define "chart.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "chart.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/metrics-certs.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/metrics-certs.yaml
new file mode 100644
index 00000000000..972b8fed25a
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/metrics-certs.yaml
@@ -0,0 +1,18 @@
+{{- if and .Values.certManager.enable .Values.metrics.enable }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-metrics-certs
+ namespace: {{ .Release.Namespace }}
+spec:
+ dnsNames:
+ - {{ include "chart.serviceName" (dict "suffix" "controller-manager-metrics-service" "context" .) }}.{{ .Release.Namespace }}.svc
+ - {{ include "chart.serviceName" (dict "suffix" "controller-manager-metrics-service" "context" .) }}.{{ .Release.Namespace }}.svc.cluster.local
+ issuerRef:
+ kind: Issuer
+ name: {{ include "chart.name" . }}-selfsigned-issuer
+ secretName: metrics-server-cert
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/selfsigned-issuer.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/selfsigned-issuer.yaml
new file mode 100644
index 00000000000..ede8ccde366
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/selfsigned-issuer.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.certManager.enable }}
+apiVersion: cert-manager.io/v1
+kind: Issuer
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-selfsigned-issuer
+ namespace: {{ .Release.Namespace }}
+spec:
+ selfSigned: {}
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/serving-cert.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/serving-cert.yaml
new file mode 100644
index 00000000000..8cc0c296f6b
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/cert-manager/serving-cert.yaml
@@ -0,0 +1,18 @@
+{{- if .Values.certManager.enable }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-serving-cert
+ namespace: {{ .Release.Namespace }}
+spec:
+ dnsNames:
+ - project-webhook-service.{{ .Release.Namespace }}.svc
+ - project-webhook-service.{{ .Release.Namespace }}.svc.cluster.local
+ issuerRef:
+ kind: Issuer
+ name: {{ include "chart.name" . }}-selfsigned-issuer
+ secretName: webhook-server-cert
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/crd/cronjobs.batch.tutorial.kubebuilder.io.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/crd/cronjobs.batch.tutorial.kubebuilder.io.yaml
new file mode 100644
index 00000000000..4df6b68185a
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/crd/cronjobs.batch.tutorial.kubebuilder.io.yaml
@@ -0,0 +1,8072 @@
+{{- if .Values.crd.enable }}
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/project-serving-cert
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: cronjobs.batch.tutorial.kubebuilder.io
+spec:
+ conversion:
+ strategy: Webhook
+ webhook:
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /convert
+ conversionReviewVersions:
+ - v1
+ group: batch.tutorial.kubebuilder.io
+ names:
+ kind: CronJob
+ listKind: CronJobList
+ plural: cronjobs
+ singular: cronjob
+ scope: Namespaced
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ concurrencyPolicy:
+ default: Allow
+ enum:
+ - Allow
+ - Forbid
+ - Replace
+ type: string
+ failedJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ jobTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ backoffLimit:
+ format: int32
+ type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
+ completions:
+ format: int32
+ type: integer
+ managedBy:
+ type: string
+ manualSelector:
+ type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
+ parallelism:
+ format: int32
+ type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
+ template:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ affinity:
+ properties:
+ nodeAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ preference:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ properties:
+ nodeSelectorTerms:
+ items:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podAntiAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ type: object
+ automountServiceAccountToken:
+ type: boolean
+ containers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ dnsConfig:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ searches:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ dnsPolicy:
+ type: string
+ enableServiceLinks:
+ type: boolean
+ ephemeralContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ targetContainerName:
+ type: string
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ hostAliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ ip:
+ type: string
+ required:
+ - ip
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
+ hostIPC:
+ type: boolean
+ hostNetwork:
+ type: boolean
+ hostPID:
+ type: boolean
+ hostUsers:
+ type: boolean
+ hostname:
+ type: string
+ hostnameOverride:
+ type: string
+ imagePullSecrets:
+ items:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ initContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ nodeName:
+ type: string
+ nodeSelector:
+ additionalProperties:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ overhead:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ preemptionPolicy:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ priorityClassName:
+ type: string
+ readinessGates:
+ items:
+ properties:
+ conditionType:
+ type: string
+ required:
+ - conditionType
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ runtimeClassName:
+ type: string
+ schedulerName:
+ type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ securityContext:
+ properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ fsGroup:
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ type: string
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxChangePolicy:
+ type: string
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
+ sysctls:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ type: string
+ serviceAccountName:
+ type: string
+ setHostnameAsFQDN:
+ type: boolean
+ shareProcessNamespace:
+ type: boolean
+ subdomain:
+ type: string
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ tolerations:
+ items:
+ properties:
+ effect:
+ type: string
+ key:
+ type: string
+ operator:
+ type: string
+ tolerationSeconds:
+ format: int64
+ type: integer
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ topologySpreadConstraints:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ format: int32
+ type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
+ topologyKey:
+ type: string
+ whenUnsatisfiable:
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
+ volumes:
+ items:
+ properties:
+ awsElasticBlockStore:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ properties:
+ cachingMode:
+ type: string
+ diskName:
+ type: string
+ diskURI:
+ type: string
+ fsType:
+ default: ext4
+ type: string
+ kind:
+ type: string
+ readOnly:
+ default: false
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ properties:
+ readOnly:
+ type: boolean
+ secretName:
+ type: string
+ shareName:
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ properties:
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ secretFile:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ csi:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ nodePublishSecretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ readOnly:
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ emptyDir:
+ properties:
+ medium:
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ properties:
+ volumeClaimTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ accessModes:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ dataSource:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ storageClassName:
+ type: string
+ volumeAttributesClassName:
+ type: string
+ volumeMode:
+ type: string
+ volumeName:
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ properties:
+ fsType:
+ type: string
+ lun:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ targetWWNs:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ wwids:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ flexVolume:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ type: object
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - driver
+ type: object
+ flocker:
+ properties:
+ datasetName:
+ type: string
+ datasetUUID:
+ type: string
+ type: object
+ gcePersistentDisk:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ pdName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ properties:
+ directory:
+ type: string
+ repository:
+ type: string
+ revision:
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ properties:
+ endpoints:
+ type: string
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ properties:
+ path:
+ type: string
+ type:
+ type: string
+ required:
+ - path
+ type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
+ iscsi:
+ properties:
+ chapAuthDiscovery:
+ type: boolean
+ chapAuthSession:
+ type: boolean
+ fsType:
+ type: string
+ initiatorName:
+ type: string
+ iqn:
+ type: string
+ iscsiInterface:
+ default: default
+ type: string
+ lun:
+ format: int32
+ type: integer
+ portals:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ targetPortal:
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ type: string
+ nfs:
+ properties:
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ server:
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ properties:
+ claimName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ properties:
+ fsType:
+ type: string
+ pdID:
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ sources:
+ items:
+ properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
+ configMap:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ properties:
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
+ type: object
+ secret:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ properties:
+ audience:
+ type: string
+ expirationSeconds:
+ format: int64
+ type: integer
+ path:
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ quobyte:
+ properties:
+ group:
+ type: string
+ readOnly:
+ type: boolean
+ registry:
+ type: string
+ tenant:
+ type: string
+ user:
+ type: string
+ volume:
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ properties:
+ fsType:
+ type: string
+ image:
+ type: string
+ keyring:
+ default: /etc/ceph/keyring
+ type: string
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ pool:
+ default: rbd
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ default: admin
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ properties:
+ fsType:
+ default: xfs
+ type: string
+ gateway:
+ type: string
+ protectionDomain:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ sslEnabled:
+ type: boolean
+ storageMode:
+ default: ThinProvisioned
+ type: string
+ storagePool:
+ type: string
+ system:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ optional:
+ type: boolean
+ secretName:
+ type: string
+ type: object
+ storageos:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeName:
+ type: string
+ volumeNamespace:
+ type: string
+ type: object
+ vsphereVolume:
+ properties:
+ fsType:
+ type: string
+ storagePolicyID:
+ type: string
+ storagePolicyName:
+ type: string
+ volumePath:
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ required:
+ - containers
+ type: object
+ type: object
+ ttlSecondsAfterFinished:
+ format: int32
+ type: integer
+ required:
+ - template
+ type: object
+ type: object
+ schedule:
+ minLength: 0
+ type: string
+ startingDeadlineSeconds:
+ format: int64
+ minimum: 0
+ type: integer
+ successfulJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ suspend:
+ type: boolean
+ required:
+ - jobTemplate
+ - schedule
+ type: object
+ status:
+ properties:
+ active:
+ items:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ resourceVersion:
+ type: string
+ uid:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ lastScheduleTime:
+ format: date-time
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ - name: v2
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ concurrencyPolicy:
+ default: Allow
+ enum:
+ - Allow
+ - Forbid
+ - Replace
+ type: string
+ failedJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ jobTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ backoffLimit:
+ format: int32
+ type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
+ completions:
+ format: int32
+ type: integer
+ managedBy:
+ type: string
+ manualSelector:
+ type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
+ parallelism:
+ format: int32
+ type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
+ template:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ affinity:
+ properties:
+ nodeAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ preference:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ properties:
+ nodeSelectorTerms:
+ items:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podAntiAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ type: object
+ automountServiceAccountToken:
+ type: boolean
+ containers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ dnsConfig:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ searches:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ dnsPolicy:
+ type: string
+ enableServiceLinks:
+ type: boolean
+ ephemeralContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ targetContainerName:
+ type: string
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ hostAliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ ip:
+ type: string
+ required:
+ - ip
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
+ hostIPC:
+ type: boolean
+ hostNetwork:
+ type: boolean
+ hostPID:
+ type: boolean
+ hostUsers:
+ type: boolean
+ hostname:
+ type: string
+ hostnameOverride:
+ type: string
+ imagePullSecrets:
+ items:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ initContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ nodeName:
+ type: string
+ nodeSelector:
+ additionalProperties:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ overhead:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ preemptionPolicy:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ priorityClassName:
+ type: string
+ readinessGates:
+ items:
+ properties:
+ conditionType:
+ type: string
+ required:
+ - conditionType
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ runtimeClassName:
+ type: string
+ schedulerName:
+ type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ securityContext:
+ properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ fsGroup:
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ type: string
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxChangePolicy:
+ type: string
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
+ sysctls:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ type: string
+ serviceAccountName:
+ type: string
+ setHostnameAsFQDN:
+ type: boolean
+ shareProcessNamespace:
+ type: boolean
+ subdomain:
+ type: string
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ tolerations:
+ items:
+ properties:
+ effect:
+ type: string
+ key:
+ type: string
+ operator:
+ type: string
+ tolerationSeconds:
+ format: int64
+ type: integer
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ topologySpreadConstraints:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ format: int32
+ type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
+ topologyKey:
+ type: string
+ whenUnsatisfiable:
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
+ volumes:
+ items:
+ properties:
+ awsElasticBlockStore:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ properties:
+ cachingMode:
+ type: string
+ diskName:
+ type: string
+ diskURI:
+ type: string
+ fsType:
+ default: ext4
+ type: string
+ kind:
+ type: string
+ readOnly:
+ default: false
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ properties:
+ readOnly:
+ type: boolean
+ secretName:
+ type: string
+ shareName:
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ properties:
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ secretFile:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ csi:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ nodePublishSecretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ readOnly:
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ emptyDir:
+ properties:
+ medium:
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ properties:
+ volumeClaimTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ accessModes:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ dataSource:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ storageClassName:
+ type: string
+ volumeAttributesClassName:
+ type: string
+ volumeMode:
+ type: string
+ volumeName:
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ properties:
+ fsType:
+ type: string
+ lun:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ targetWWNs:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ wwids:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ flexVolume:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ type: object
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - driver
+ type: object
+ flocker:
+ properties:
+ datasetName:
+ type: string
+ datasetUUID:
+ type: string
+ type: object
+ gcePersistentDisk:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ pdName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ properties:
+ directory:
+ type: string
+ repository:
+ type: string
+ revision:
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ properties:
+ endpoints:
+ type: string
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ properties:
+ path:
+ type: string
+ type:
+ type: string
+ required:
+ - path
+ type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
+ iscsi:
+ properties:
+ chapAuthDiscovery:
+ type: boolean
+ chapAuthSession:
+ type: boolean
+ fsType:
+ type: string
+ initiatorName:
+ type: string
+ iqn:
+ type: string
+ iscsiInterface:
+ default: default
+ type: string
+ lun:
+ format: int32
+ type: integer
+ portals:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ targetPortal:
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ type: string
+ nfs:
+ properties:
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ server:
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ properties:
+ claimName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ properties:
+ fsType:
+ type: string
+ pdID:
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ sources:
+ items:
+ properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
+ configMap:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ properties:
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
+ type: object
+ secret:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ properties:
+ audience:
+ type: string
+ expirationSeconds:
+ format: int64
+ type: integer
+ path:
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ quobyte:
+ properties:
+ group:
+ type: string
+ readOnly:
+ type: boolean
+ registry:
+ type: string
+ tenant:
+ type: string
+ user:
+ type: string
+ volume:
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ properties:
+ fsType:
+ type: string
+ image:
+ type: string
+ keyring:
+ default: /etc/ceph/keyring
+ type: string
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ pool:
+ default: rbd
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ default: admin
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ properties:
+ fsType:
+ default: xfs
+ type: string
+ gateway:
+ type: string
+ protectionDomain:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ sslEnabled:
+ type: boolean
+ storageMode:
+ default: ThinProvisioned
+ type: string
+ storagePool:
+ type: string
+ system:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ optional:
+ type: boolean
+ secretName:
+ type: string
+ type: object
+ storageos:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeName:
+ type: string
+ volumeNamespace:
+ type: string
+ type: object
+ vsphereVolume:
+ properties:
+ fsType:
+ type: string
+ storagePolicyID:
+ type: string
+ storagePolicyName:
+ type: string
+ volumePath:
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ required:
+ - containers
+ type: object
+ type: object
+ ttlSecondsAfterFinished:
+ format: int32
+ type: integer
+ required:
+ - template
+ type: object
+ type: object
+ schedule:
+ properties:
+ dayOfMonth:
+ type: string
+ dayOfWeek:
+ type: string
+ hour:
+ type: string
+ minute:
+ type: string
+ month:
+ type: string
+ type: object
+ startingDeadlineSeconds:
+ format: int64
+ minimum: 0
+ type: integer
+ successfulJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ suspend:
+ type: boolean
+ required:
+ - jobTemplate
+ - schedule
+ type: object
+ status:
+ properties:
+ active:
+ items:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ resourceVersion:
+ type: string
+ uid:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ lastScheduleTime:
+ format: date-time
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: false
+ subresources:
+ status: {}
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/manager/manager.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/manager/manager.yaml
new file mode 100644
index 00000000000..635890c0934
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/manager/manager.yaml
@@ -0,0 +1,112 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
+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:
+ - args:
+ {{- if .Values.metrics.enable }}
+ - --metrics-bind-address=:{{ .Values.metrics.port }}
+ {{- else }}
+ # Bind to :0 to disable the controller-runtime managed metrics server
+ - --metrics-bind-address=0
+ {{- end }}
+ - --health-probe-bind-address=:8081
+ {{- range .Values.manager.args }}
+ - {{ . }}
+ {{- end }}
+ {{- if and .Values.certManager.enable .Values.metrics.enable }}
+ - --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs
+ {{- end }}
+ {{- if .Values.certManager.enable }}
+ - --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs
+ {{- end }}
+ command:
+ - /manager
+ image: "{{ .Values.manager.image.repository }}:{{ .Values.manager.image.tag }}"
+ imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ ports:
+ - containerPort: {{ .Values.webhook.port }}
+ name: webhook-server
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ {{- if .Values.manager.resources }}
+ {{- toYaml .Values.manager.resources | nindent 20 }}
+ {{- else }}
+ {}
+ {{- end }}
+ securityContext:
+ {{- if .Values.manager.securityContext }}
+ {{- toYaml .Values.manager.securityContext | nindent 20 }}
+ {{- else }}
+ {}
+ {{- end }}
+ volumeMounts:
+ {{- if and .Values.certManager.enable .Values.metrics.enable }}
+ - mountPath: /tmp/k8s-metrics-server/metrics-certs
+ name: metrics-certs
+ readOnly: true
+ {{- end }}
+ {{- if .Values.certManager.enable }}
+ - mountPath: /tmp/k8s-webhook-server/serving-certs
+ name: webhook-certs
+ readOnly: true
+ {{- end }}
+ securityContext:
+ {{- if .Values.manager.podSecurityContext }}
+ {{- toYaml .Values.manager.podSecurityContext | nindent 14 }}
+ {{- else }}
+ {}
+ {{- end }}
+ serviceAccountName: project-controller-manager
+ terminationGracePeriodSeconds: 10
+ volumes:
+ {{- if and .Values.certManager.enable .Values.metrics.enable }}
+ - 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
+ {{- end }}
+ {{- if .Values.certManager.enable }}
+ - name: webhook-certs
+ secret:
+ secretName: webhook-server-cert
+ {{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml
new file mode 100644
index 00000000000..c5b24a9125a
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/metrics/controller-manager-metrics-service.yaml
@@ -0,0 +1,20 @@
+{{- if .Values.metrics.enable }}
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-service
+ namespace: {{ .Release.Namespace }}
+spec:
+ ports:
+ - name: https
+ port: {{ .Values.metrics.port }}
+ protocol: TCP
+ targetPort: {{ .Values.metrics.port }}
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/prometheus/controller-manager-metrics-monitor.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/prometheus/controller-manager-metrics-monitor.yaml
new file mode 100644
index 00000000000..c8e2b22c26e
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/prometheus/controller-manager-metrics-monitor.yaml
@@ -0,0 +1,35 @@
+{{- if .Values.prometheus.enable }}
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-monitor
+ namespace: {{ .Release.Namespace }}
+spec:
+ endpoints:
+ - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+ path: /metrics
+ port: https
+ scheme: https
+ 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
+ serverName: project-controller-manager-metrics-service.{{ .Release.Namespace }}.svc
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/controller-manager.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/controller-manager.yaml
new file mode 100644
index 00000000000..a757441625d
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/controller-manager.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-admin-role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-admin-role.yaml
new file mode 100644
index 00000000000..f0d8d50f4de
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-admin-role.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ 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
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-editor-role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-editor-role.yaml
new file mode 100644
index 00000000000..b9e0db896d6
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-editor-role.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-cronjob-editor-role
+rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-viewer-role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-viewer-role.yaml
new file mode 100644
index 00000000000..88e43a7d001
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/cronjob-viewer-role.yaml
@@ -0,0 +1,24 @@
+{{- if .Values.rbacHelpers.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-cronjob-viewer-role
+rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - get
+ - list
+ - watch
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml
new file mode 100644
index 00000000000..17bb2531ff8
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-role.yaml
@@ -0,0 +1,40 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-leader-election-role
+ namespace: {{ .Release.Namespace }}
+rules:
+ - apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+ - apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+ - apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml
new file mode 100644
index 00000000000..4966c4ac459
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/leader-election-rolebinding.yaml
@@ -0,0 +1,16 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-leader-election-rolebinding
+ namespace: {{ .Release.Namespace }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: project-leader-election-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/manager-role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/manager-role.yaml
new file mode 100644
index 00000000000..4261a27fa49
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/manager-role.yaml
@@ -0,0 +1,49 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-manager-role
+rules:
+ - apiGroups:
+ - batch
+ resources:
+ - jobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - batch
+ resources:
+ - jobs/status
+ verbs:
+ - get
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/finalizers
+ verbs:
+ - update
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+ - patch
+ - update
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml
new file mode 100644
index 00000000000..b82536997bc
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/manager-rolebinding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-manager-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml
new file mode 100644
index 00000000000..5954a82944e
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-role.yaml
@@ -0,0 +1,19 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-auth-role
+rules:
+ - apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+ - apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml
new file mode 100644
index 00000000000..33549b4c5ba
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-auth-rolebinding.yaml
@@ -0,0 +1,14 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: project-metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-metrics-auth-role
+subjects:
+ - kind: ServiceAccount
+ name: project-controller-manager
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml
new file mode 100644
index 00000000000..609835fe71f
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/rbac/metrics-reader.yaml
@@ -0,0 +1,11 @@
+{{- if .Values.metrics.enable }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-reader
+rules:
+ - nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+{{- end }}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/mutating-webhook-configuration.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/mutating-webhook-configuration.yaml
new file mode 100644
index 00000000000..53b847b7a29
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/mutating-webhook-configuration.yaml
@@ -0,0 +1,49 @@
+apiVersion: admissionregistration.k8s.io/v1
+kind: MutatingWebhookConfiguration
+metadata:
+ annotations:
+ {{- if .Values.certManager.enable }}
+ cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/project-serving-cert
+ {{- end }}
+ name: project-mutating-webhook-configuration
+webhooks:
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /mutate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /mutate-batch-tutorial-kubebuilder-io-v2-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v2.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/validating-webhook-configuration.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/validating-webhook-configuration.yaml
new file mode 100644
index 00000000000..566f7b513d5
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/validating-webhook-configuration.yaml
@@ -0,0 +1,49 @@
+apiVersion: admissionregistration.k8s.io/v1
+kind: ValidatingWebhookConfiguration
+metadata:
+ annotations:
+ {{- if .Values.certManager.enable }}
+ cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/project-serving-cert
+ {{- end }}
+ name: project-validating-webhook-configuration
+webhooks:
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /validate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+ - admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+ path: /validate-batch-tutorial-kubebuilder-io-v2-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v2.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/webhook-service.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/webhook-service.yaml
new file mode 100644
index 00000000000..99213498502
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/templates/webhook/webhook-service.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
+ app.kubernetes.io/name: project
+ name: project-webhook-service
+ namespace: {{ .Release.Namespace }}
+spec:
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: {{ .Values.webhook.port }}
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/values.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/values.yaml
new file mode 100644
index 00000000000..e9cb6515c95
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/chart/values.yaml
@@ -0,0 +1,75 @@
+# Configure the controller manager deployment
+manager:
+ replicas: 1
+
+ image:
+ repository: controller
+ tag: latest
+ pullPolicy: IfNotPresent
+
+ # Arguments
+ args:
+ - --leader-elect
+
+ # Environment variables
+ env: []
+
+ # Pod-level security settings
+ podSecurityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+
+ # Container-level security settings
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+
+ # Resource limits and requests
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+
+# Essential RBAC permissions (required for controller operation)
+# These include ServiceAccount, controller permissions, leader election, and metrics access
+# Note: Essential RBAC is always enabled as it's required for the controller to function
+
+# Helper RBAC roles for managing custom resources
+# These provide convenient admin/editor/viewer roles for each CRD type
+# Useful for giving users different levels of access to your custom resources
+rbacHelpers:
+ enable: false # Install convenience admin/editor/viewer roles for CRDs
+
+# Custom Resource Definitions
+crd:
+ enable: true # Install CRDs with the chart
+ keep: true # Keep CRDs when uninstalling
+
+# Controller metrics endpoint.
+# Enable to expose /metrics endpoint with RBAC protection.
+metrics:
+ enable: true
+ port: 8443 # Metrics server port
+
+# Cert-manager integration for TLS certificates.
+# Required for webhook certificates and metrics endpoint certificates.
+certManager:
+ enable: true
+
+# Webhook server configuration
+webhook:
+ enable: true
+ port: 9443 # Webhook server port
+
+# Prometheus ServiceMonitor for metrics scraping.
+# Requires prometheus-operator to be installed in the cluster.
+prometheus:
+ enable: false
+
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml b/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml
new file mode 100644
index 00000000000..b4310f3ef7a
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/dist/install.yaml
@@ -0,0 +1,8627 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-system
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ cert-manager.io/inject-ca-from: project-system/project-serving-cert
+ controller-gen.kubebuilder.io/version: v0.19.0
+ name: cronjobs.batch.tutorial.kubebuilder.io
+spec:
+ conversion:
+ strategy: Webhook
+ webhook:
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /convert
+ conversionReviewVersions:
+ - v1
+ group: batch.tutorial.kubebuilder.io
+ names:
+ kind: CronJob
+ listKind: CronJobList
+ plural: cronjobs
+ singular: cronjob
+ scope: Namespaced
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ concurrencyPolicy:
+ default: Allow
+ enum:
+ - Allow
+ - Forbid
+ - Replace
+ type: string
+ failedJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ jobTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ backoffLimit:
+ format: int32
+ type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
+ completions:
+ format: int32
+ type: integer
+ managedBy:
+ type: string
+ manualSelector:
+ type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
+ parallelism:
+ format: int32
+ type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
+ template:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ affinity:
+ properties:
+ nodeAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ preference:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ properties:
+ nodeSelectorTerms:
+ items:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podAntiAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ type: object
+ automountServiceAccountToken:
+ type: boolean
+ containers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ dnsConfig:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ searches:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ dnsPolicy:
+ type: string
+ enableServiceLinks:
+ type: boolean
+ ephemeralContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ targetContainerName:
+ type: string
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ hostAliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ ip:
+ type: string
+ required:
+ - ip
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
+ hostIPC:
+ type: boolean
+ hostNetwork:
+ type: boolean
+ hostPID:
+ type: boolean
+ hostUsers:
+ type: boolean
+ hostname:
+ type: string
+ hostnameOverride:
+ type: string
+ imagePullSecrets:
+ items:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ initContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ nodeName:
+ type: string
+ nodeSelector:
+ additionalProperties:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ overhead:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ preemptionPolicy:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ priorityClassName:
+ type: string
+ readinessGates:
+ items:
+ properties:
+ conditionType:
+ type: string
+ required:
+ - conditionType
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ runtimeClassName:
+ type: string
+ schedulerName:
+ type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ securityContext:
+ properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ fsGroup:
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ type: string
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxChangePolicy:
+ type: string
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
+ sysctls:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ type: string
+ serviceAccountName:
+ type: string
+ setHostnameAsFQDN:
+ type: boolean
+ shareProcessNamespace:
+ type: boolean
+ subdomain:
+ type: string
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ tolerations:
+ items:
+ properties:
+ effect:
+ type: string
+ key:
+ type: string
+ operator:
+ type: string
+ tolerationSeconds:
+ format: int64
+ type: integer
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ topologySpreadConstraints:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ format: int32
+ type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
+ topologyKey:
+ type: string
+ whenUnsatisfiable:
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
+ volumes:
+ items:
+ properties:
+ awsElasticBlockStore:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ properties:
+ cachingMode:
+ type: string
+ diskName:
+ type: string
+ diskURI:
+ type: string
+ fsType:
+ default: ext4
+ type: string
+ kind:
+ type: string
+ readOnly:
+ default: false
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ properties:
+ readOnly:
+ type: boolean
+ secretName:
+ type: string
+ shareName:
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ properties:
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ secretFile:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ csi:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ nodePublishSecretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ readOnly:
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ emptyDir:
+ properties:
+ medium:
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ properties:
+ volumeClaimTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ accessModes:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ dataSource:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ storageClassName:
+ type: string
+ volumeAttributesClassName:
+ type: string
+ volumeMode:
+ type: string
+ volumeName:
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ properties:
+ fsType:
+ type: string
+ lun:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ targetWWNs:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ wwids:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ flexVolume:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ type: object
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - driver
+ type: object
+ flocker:
+ properties:
+ datasetName:
+ type: string
+ datasetUUID:
+ type: string
+ type: object
+ gcePersistentDisk:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ pdName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ properties:
+ directory:
+ type: string
+ repository:
+ type: string
+ revision:
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ properties:
+ endpoints:
+ type: string
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ properties:
+ path:
+ type: string
+ type:
+ type: string
+ required:
+ - path
+ type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
+ iscsi:
+ properties:
+ chapAuthDiscovery:
+ type: boolean
+ chapAuthSession:
+ type: boolean
+ fsType:
+ type: string
+ initiatorName:
+ type: string
+ iqn:
+ type: string
+ iscsiInterface:
+ default: default
+ type: string
+ lun:
+ format: int32
+ type: integer
+ portals:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ targetPortal:
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ type: string
+ nfs:
+ properties:
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ server:
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ properties:
+ claimName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ properties:
+ fsType:
+ type: string
+ pdID:
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ sources:
+ items:
+ properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
+ configMap:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ properties:
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
+ type: object
+ secret:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ properties:
+ audience:
+ type: string
+ expirationSeconds:
+ format: int64
+ type: integer
+ path:
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ quobyte:
+ properties:
+ group:
+ type: string
+ readOnly:
+ type: boolean
+ registry:
+ type: string
+ tenant:
+ type: string
+ user:
+ type: string
+ volume:
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ properties:
+ fsType:
+ type: string
+ image:
+ type: string
+ keyring:
+ default: /etc/ceph/keyring
+ type: string
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ pool:
+ default: rbd
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ default: admin
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ properties:
+ fsType:
+ default: xfs
+ type: string
+ gateway:
+ type: string
+ protectionDomain:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ sslEnabled:
+ type: boolean
+ storageMode:
+ default: ThinProvisioned
+ type: string
+ storagePool:
+ type: string
+ system:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ optional:
+ type: boolean
+ secretName:
+ type: string
+ type: object
+ storageos:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeName:
+ type: string
+ volumeNamespace:
+ type: string
+ type: object
+ vsphereVolume:
+ properties:
+ fsType:
+ type: string
+ storagePolicyID:
+ type: string
+ storagePolicyName:
+ type: string
+ volumePath:
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ required:
+ - containers
+ type: object
+ type: object
+ ttlSecondsAfterFinished:
+ format: int32
+ type: integer
+ required:
+ - template
+ type: object
+ type: object
+ schedule:
+ minLength: 0
+ type: string
+ startingDeadlineSeconds:
+ format: int64
+ minimum: 0
+ type: integer
+ successfulJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ suspend:
+ type: boolean
+ required:
+ - jobTemplate
+ - schedule
+ type: object
+ status:
+ properties:
+ active:
+ items:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ resourceVersion:
+ type: string
+ uid:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ lastScheduleTime:
+ format: date-time
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ - name: v2
+ schema:
+ openAPIV3Schema:
+ properties:
+ apiVersion:
+ type: string
+ kind:
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ concurrencyPolicy:
+ default: Allow
+ enum:
+ - Allow
+ - Forbid
+ - Replace
+ type: string
+ failedJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ jobTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ backoffLimit:
+ format: int32
+ type: integer
+ backoffLimitPerIndex:
+ format: int32
+ type: integer
+ completionMode:
+ type: string
+ completions:
+ format: int32
+ type: integer
+ managedBy:
+ type: string
+ manualSelector:
+ type: boolean
+ maxFailedIndexes:
+ format: int32
+ type: integer
+ parallelism:
+ format: int32
+ type: integer
+ podFailurePolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ action:
+ type: string
+ onExitCodes:
+ properties:
+ containerName:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ - values
+ type: object
+ onPodConditions:
+ items:
+ properties:
+ status:
+ type: string
+ type:
+ type: string
+ required:
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ podReplacementPolicy:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ successPolicy:
+ properties:
+ rules:
+ items:
+ properties:
+ succeededCount:
+ format: int32
+ type: integer
+ succeededIndexes:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - rules
+ type: object
+ suspend:
+ type: boolean
+ template:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ activeDeadlineSeconds:
+ format: int64
+ type: integer
+ affinity:
+ properties:
+ nodeAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ preference:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ properties:
+ nodeSelectorTerms:
+ items:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchFields:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podAntiAffinity:
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ podAffinityTerm:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ requiredDuringSchedulingIgnoredDuringExecution:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ namespaces:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ topologyKey:
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ type: object
+ automountServiceAccountToken:
+ type: boolean
+ containers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ dnsConfig:
+ properties:
+ nameservers:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ options:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ searches:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ dnsPolicy:
+ type: string
+ enableServiceLinks:
+ type: boolean
+ ephemeralContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ targetContainerName:
+ type: string
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ hostAliases:
+ items:
+ properties:
+ hostnames:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ ip:
+ type: string
+ required:
+ - ip
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - ip
+ x-kubernetes-list-type: map
+ hostIPC:
+ type: boolean
+ hostNetwork:
+ type: boolean
+ hostPID:
+ type: boolean
+ hostUsers:
+ type: boolean
+ hostname:
+ type: string
+ hostnameOverride:
+ type: string
+ imagePullSecrets:
+ items:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ initContainers:
+ items:
+ properties:
+ args:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ env:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ valueFrom:
+ properties:
+ configMapKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ fileKeyRef:
+ properties:
+ key:
+ type: string
+ optional:
+ default: false
+ type: boolean
+ path:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - key
+ - path
+ - volumeName
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ properties:
+ key:
+ type: string
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ envFrom:
+ items:
+ properties:
+ configMapRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ image:
+ type: string
+ imagePullPolicy:
+ type: string
+ lifecycle:
+ properties:
+ postStart:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ preStop:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ sleep:
+ properties:
+ seconds:
+ format: int64
+ type: integer
+ required:
+ - seconds
+ type: object
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: object
+ stopSignal:
+ type: string
+ type: object
+ livenessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ name:
+ type: string
+ ports:
+ items:
+ properties:
+ containerPort:
+ format: int32
+ type: integer
+ hostIP:
+ type: string
+ hostPort:
+ format: int32
+ type: integer
+ name:
+ type: string
+ protocol:
+ default: TCP
+ type: string
+ required:
+ - containerPort
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - containerPort
+ - protocol
+ x-kubernetes-list-type: map
+ readinessProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ resizePolicy:
+ items:
+ properties:
+ resourceName:
+ type: string
+ restartPolicy:
+ type: string
+ required:
+ - resourceName
+ - restartPolicy
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ restartPolicyRules:
+ items:
+ properties:
+ action:
+ type: string
+ exitCodes:
+ properties:
+ operator:
+ type: string
+ values:
+ items:
+ format: int32
+ type: integer
+ type: array
+ x-kubernetes-list-type: set
+ required:
+ - operator
+ type: object
+ required:
+ - action
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ securityContext:
+ properties:
+ allowPrivilegeEscalation:
+ type: boolean
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ capabilities:
+ properties:
+ add:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ drop:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ privileged:
+ type: boolean
+ procMount:
+ type: string
+ readOnlyRootFilesystem:
+ type: boolean
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ startupProbe:
+ properties:
+ exec:
+ properties:
+ command:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ failureThreshold:
+ format: int32
+ type: integer
+ grpc:
+ properties:
+ port:
+ format: int32
+ type: integer
+ service:
+ default: ""
+ type: string
+ required:
+ - port
+ type: object
+ httpGet:
+ properties:
+ host:
+ type: string
+ httpHeaders:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ scheme:
+ type: string
+ required:
+ - port
+ type: object
+ initialDelaySeconds:
+ format: int32
+ type: integer
+ periodSeconds:
+ format: int32
+ type: integer
+ successThreshold:
+ format: int32
+ type: integer
+ tcpSocket:
+ properties:
+ host:
+ type: string
+ port:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ timeoutSeconds:
+ format: int32
+ type: integer
+ type: object
+ stdin:
+ type: boolean
+ stdinOnce:
+ type: boolean
+ terminationMessagePath:
+ type: string
+ terminationMessagePolicy:
+ type: string
+ tty:
+ type: boolean
+ volumeDevices:
+ items:
+ properties:
+ devicePath:
+ type: string
+ name:
+ type: string
+ required:
+ - devicePath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - devicePath
+ x-kubernetes-list-type: map
+ volumeMounts:
+ items:
+ properties:
+ mountPath:
+ type: string
+ mountPropagation:
+ type: string
+ name:
+ type: string
+ readOnly:
+ type: boolean
+ recursiveReadOnly:
+ type: string
+ subPath:
+ type: string
+ subPathExpr:
+ type: string
+ required:
+ - mountPath
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - mountPath
+ x-kubernetes-list-type: map
+ workingDir:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ nodeName:
+ type: string
+ nodeSelector:
+ additionalProperties:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ os:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ overhead:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ preemptionPolicy:
+ type: string
+ priority:
+ format: int32
+ type: integer
+ priorityClassName:
+ type: string
+ readinessGates:
+ items:
+ properties:
+ conditionType:
+ type: string
+ required:
+ - conditionType
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ resourceClaims:
+ items:
+ properties:
+ name:
+ type: string
+ resourceClaimName:
+ type: string
+ resourceClaimTemplateName:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ resources:
+ properties:
+ claims:
+ items:
+ properties:
+ name:
+ type: string
+ request:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ restartPolicy:
+ type: string
+ runtimeClassName:
+ type: string
+ schedulerName:
+ type: string
+ schedulingGates:
+ items:
+ properties:
+ name:
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ securityContext:
+ properties:
+ appArmorProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ fsGroup:
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ type: string
+ runAsGroup:
+ format: int64
+ type: integer
+ runAsNonRoot:
+ type: boolean
+ runAsUser:
+ format: int64
+ type: integer
+ seLinuxChangePolicy:
+ type: string
+ seLinuxOptions:
+ properties:
+ level:
+ type: string
+ role:
+ type: string
+ type:
+ type: string
+ user:
+ type: string
+ type: object
+ seccompProfile:
+ properties:
+ localhostProfile:
+ type: string
+ type:
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ items:
+ format: int64
+ type: integer
+ type: array
+ x-kubernetes-list-type: atomic
+ supplementalGroupsPolicy:
+ type: string
+ sysctls:
+ items:
+ properties:
+ name:
+ type: string
+ value:
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ windowsOptions:
+ properties:
+ gmsaCredentialSpec:
+ type: string
+ gmsaCredentialSpecName:
+ type: string
+ hostProcess:
+ type: boolean
+ runAsUserName:
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ type: string
+ serviceAccountName:
+ type: string
+ setHostnameAsFQDN:
+ type: boolean
+ shareProcessNamespace:
+ type: boolean
+ subdomain:
+ type: string
+ terminationGracePeriodSeconds:
+ format: int64
+ type: integer
+ tolerations:
+ items:
+ properties:
+ effect:
+ type: string
+ key:
+ type: string
+ operator:
+ type: string
+ tolerationSeconds:
+ format: int64
+ type: integer
+ value:
+ type: string
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ topologySpreadConstraints:
+ items:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ matchLabelKeys:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ format: int32
+ type: integer
+ minDomains:
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ type: string
+ nodeTaintsPolicy:
+ type: string
+ topologyKey:
+ type: string
+ whenUnsatisfiable:
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - topologyKey
+ - whenUnsatisfiable
+ x-kubernetes-list-type: map
+ volumes:
+ items:
+ properties:
+ awsElasticBlockStore:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ azureDisk:
+ properties:
+ cachingMode:
+ type: string
+ diskName:
+ type: string
+ diskURI:
+ type: string
+ fsType:
+ default: ext4
+ type: string
+ kind:
+ type: string
+ readOnly:
+ default: false
+ type: boolean
+ required:
+ - diskName
+ - diskURI
+ type: object
+ azureFile:
+ properties:
+ readOnly:
+ type: boolean
+ secretName:
+ type: string
+ shareName:
+ type: string
+ required:
+ - secretName
+ - shareName
+ type: object
+ cephfs:
+ properties:
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ secretFile:
+ type: string
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ type: string
+ required:
+ - monitors
+ type: object
+ cinder:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ configMap:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ csi:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ nodePublishSecretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ readOnly:
+ type: boolean
+ volumeAttributes:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - driver
+ type: object
+ downwardAPI:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ emptyDir:
+ properties:
+ medium:
+ type: string
+ sizeLimit:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ ephemeral:
+ properties:
+ volumeClaimTemplate:
+ properties:
+ metadata:
+ type: object
+ spec:
+ properties:
+ accessModes:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ dataSource:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ x-kubernetes-map-type: atomic
+ dataSourceRef:
+ properties:
+ apiGroup:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ resources:
+ properties:
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ type: object
+ type: object
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ storageClassName:
+ type: string
+ volumeAttributesClassName:
+ type: string
+ volumeMode:
+ type: string
+ volumeName:
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ type: object
+ fc:
+ properties:
+ fsType:
+ type: string
+ lun:
+ format: int32
+ type: integer
+ readOnly:
+ type: boolean
+ targetWWNs:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ wwids:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ flexVolume:
+ properties:
+ driver:
+ type: string
+ fsType:
+ type: string
+ options:
+ additionalProperties:
+ type: string
+ type: object
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - driver
+ type: object
+ flocker:
+ properties:
+ datasetName:
+ type: string
+ datasetUUID:
+ type: string
+ type: object
+ gcePersistentDisk:
+ properties:
+ fsType:
+ type: string
+ partition:
+ format: int32
+ type: integer
+ pdName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - pdName
+ type: object
+ gitRepo:
+ properties:
+ directory:
+ type: string
+ repository:
+ type: string
+ revision:
+ type: string
+ required:
+ - repository
+ type: object
+ glusterfs:
+ properties:
+ endpoints:
+ type: string
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - endpoints
+ - path
+ type: object
+ hostPath:
+ properties:
+ path:
+ type: string
+ type:
+ type: string
+ required:
+ - path
+ type: object
+ image:
+ properties:
+ pullPolicy:
+ type: string
+ reference:
+ type: string
+ type: object
+ iscsi:
+ properties:
+ chapAuthDiscovery:
+ type: boolean
+ chapAuthSession:
+ type: boolean
+ fsType:
+ type: string
+ initiatorName:
+ type: string
+ iqn:
+ type: string
+ iscsiInterface:
+ default: default
+ type: string
+ lun:
+ format: int32
+ type: integer
+ portals:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ targetPortal:
+ type: string
+ required:
+ - iqn
+ - lun
+ - targetPortal
+ type: object
+ name:
+ type: string
+ nfs:
+ properties:
+ path:
+ type: string
+ readOnly:
+ type: boolean
+ server:
+ type: string
+ required:
+ - path
+ - server
+ type: object
+ persistentVolumeClaim:
+ properties:
+ claimName:
+ type: string
+ readOnly:
+ type: boolean
+ required:
+ - claimName
+ type: object
+ photonPersistentDisk:
+ properties:
+ fsType:
+ type: string
+ pdID:
+ type: string
+ required:
+ - pdID
+ type: object
+ portworxVolume:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ volumeID:
+ type: string
+ required:
+ - volumeID
+ type: object
+ projected:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ sources:
+ items:
+ properties:
+ clusterTrustBundle:
+ properties:
+ labelSelector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ x-kubernetes-map-type: atomic
+ name:
+ type: string
+ optional:
+ type: boolean
+ path:
+ type: string
+ signerName:
+ type: string
+ required:
+ - path
+ type: object
+ configMap:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ properties:
+ items:
+ items:
+ properties:
+ fieldRef:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ resourceFieldRef:
+ properties:
+ containerName:
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ podCertificate:
+ properties:
+ certificateChainPath:
+ type: string
+ credentialBundlePath:
+ type: string
+ keyPath:
+ type: string
+ keyType:
+ type: string
+ maxExpirationSeconds:
+ format: int32
+ type: integer
+ signerName:
+ type: string
+ required:
+ - keyType
+ - signerName
+ type: object
+ secret:
+ properties:
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ name:
+ default: ""
+ type: string
+ optional:
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ properties:
+ audience:
+ type: string
+ expirationSeconds:
+ format: int64
+ type: integer
+ path:
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ type: object
+ quobyte:
+ properties:
+ group:
+ type: string
+ readOnly:
+ type: boolean
+ registry:
+ type: string
+ tenant:
+ type: string
+ user:
+ type: string
+ volume:
+ type: string
+ required:
+ - registry
+ - volume
+ type: object
+ rbd:
+ properties:
+ fsType:
+ type: string
+ image:
+ type: string
+ keyring:
+ default: /etc/ceph/keyring
+ type: string
+ monitors:
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ pool:
+ default: rbd
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ user:
+ default: admin
+ type: string
+ required:
+ - image
+ - monitors
+ type: object
+ scaleIO:
+ properties:
+ fsType:
+ default: xfs
+ type: string
+ gateway:
+ type: string
+ protectionDomain:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ sslEnabled:
+ type: boolean
+ storageMode:
+ default: ThinProvisioned
+ type: string
+ storagePool:
+ type: string
+ system:
+ type: string
+ volumeName:
+ type: string
+ required:
+ - gateway
+ - secretRef
+ - system
+ type: object
+ secret:
+ properties:
+ defaultMode:
+ format: int32
+ type: integer
+ items:
+ items:
+ properties:
+ key:
+ type: string
+ mode:
+ format: int32
+ type: integer
+ path:
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ x-kubernetes-list-type: atomic
+ optional:
+ type: boolean
+ secretName:
+ type: string
+ type: object
+ storageos:
+ properties:
+ fsType:
+ type: string
+ readOnly:
+ type: boolean
+ secretRef:
+ properties:
+ name:
+ default: ""
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ volumeName:
+ type: string
+ volumeNamespace:
+ type: string
+ type: object
+ vsphereVolume:
+ properties:
+ fsType:
+ type: string
+ storagePolicyID:
+ type: string
+ storagePolicyName:
+ type: string
+ volumePath:
+ type: string
+ required:
+ - volumePath
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ required:
+ - containers
+ type: object
+ type: object
+ ttlSecondsAfterFinished:
+ format: int32
+ type: integer
+ required:
+ - template
+ type: object
+ type: object
+ schedule:
+ properties:
+ dayOfMonth:
+ type: string
+ dayOfWeek:
+ type: string
+ hour:
+ type: string
+ minute:
+ type: string
+ month:
+ type: string
+ type: object
+ startingDeadlineSeconds:
+ format: int64
+ minimum: 0
+ type: integer
+ successfulJobsHistoryLimit:
+ format: int32
+ minimum: 0
+ type: integer
+ suspend:
+ type: boolean
+ required:
+ - jobTemplate
+ - schedule
+ type: object
+ status:
+ properties:
+ active:
+ items:
+ properties:
+ apiVersion:
+ type: string
+ fieldPath:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ namespace:
+ type: string
+ resourceVersion:
+ type: string
+ uid:
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ maxItems: 10
+ minItems: 1
+ type: array
+ x-kubernetes-list-type: atomic
+ conditions:
+ items:
+ properties:
+ lastTransitionTime:
+ format: date-time
+ type: string
+ message:
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - type
+ x-kubernetes-list-type: map
+ lastScheduleTime:
+ format: date-time
+ type: string
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: false
+ subresources:
+ status: {}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-leader-election-role
+ namespace: project-system
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+---
+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
+ app.kubernetes.io/name: project
+ name: project-cronjob-editor-role
+rules:
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- 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
+ app.kubernetes.io/name: project
+ name: project-cronjob-viewer-role
+rules:
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - get
+ - list
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-manager-role
+rules:
+- apiGroups:
+ - batch
+ resources:
+ - jobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch
+ resources:
+ - jobs/status
+ verbs:
+ - get
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - batch.tutorial.kubebuilder.io
+ resources:
+ - cronjobs/status
+ verbs:
+ - get
+ - patch
+ - update
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-auth-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: project-metrics-reader
+rules:
+- nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-leader-election-rolebinding
+ namespace: project-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: project-leader-election-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-manager-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: project-metrics-auth-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: project-metrics-auth-role
+subjects:
+- kind: ServiceAccount
+ name: project-controller-manager
+ namespace: project-system
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager-metrics-service
+ namespace: project-system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: 8443
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ name: project-webhook-service
+ namespace: project-system
+spec:
+ ports:
+ - port: 443
+ protocol: TCP
+ targetPort: 9443
+ selector:
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: project
+ control-plane: controller-manager
+ name: project-controller-manager
+ namespace: project-system
+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:
+ - args:
+ - --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
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ ports:
+ - containerPort: 9443
+ name: webhook-server
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+ volumeMounts:
+ - mountPath: /tmp/k8s-metrics-server/metrics-certs
+ name: metrics-certs
+ readOnly: true
+ - mountPath: /tmp/k8s-webhook-server/serving-certs
+ name: webhook-certs
+ readOnly: true
+ securityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+ serviceAccountName: project-controller-manager
+ terminationGracePeriodSeconds: 10
+ volumes:
+ - 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:
+ 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:
+ - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+ path: /metrics
+ port: https
+ scheme: https
+ 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
+ serverName: project-controller-manager-metrics-service.project-system.svc
+ 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:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /mutate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /mutate-batch-tutorial-kubebuilder-io-v2-cronjob
+ failurePolicy: Fail
+ name: mcronjob-v2.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+---
+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:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /validate-batch-tutorial-kubebuilder-io-v1-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v1.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v1
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
+- admissionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: project-webhook-service
+ namespace: project-system
+ path: /validate-batch-tutorial-kubebuilder-io-v2-cronjob
+ failurePolicy: Fail
+ name: vcronjob-v2.kb.io
+ rules:
+ - apiGroups:
+ - batch.tutorial.kubebuilder.io
+ apiVersions:
+ - v2
+ operations:
+ - CREATE
+ - UPDATE
+ resources:
+ - cronjobs
+ sideEffects: None
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/go.mod b/docs/book/src/multiversion-tutorial/testdata/project/go.mod
index 50f3f11f644..d4178d4c180 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/go.mod
+++ b/docs/book/src/multiversion-tutorial/testdata/project/go.mod
@@ -1,14 +1,101 @@
module tutorial.kubebuilder.io/project
-go 1.15
+go 1.24.6
require (
- github.com/go-logr/logr v0.3.0
- github.com/onsi/ginkgo v1.14.1
- github.com/onsi/gomega v1.10.2
+ github.com/onsi/ginkgo/v2 v2.22.0
+ github.com/onsi/gomega v1.36.1
github.com/robfig/cron v1.2.0
- k8s.io/api v0.19.2
- k8s.io/apimachinery v0.19.2
- k8s.io/client-go v0.19.2
- sigs.k8s.io/controller-runtime v0.7.2
+ k8s.io/api v0.34.1
+ k8s.io/apimachinery v0.34.1
+ k8s.io/client-go v0.34.1
+ sigs.k8s.io/controller-runtime v0.22.4
+)
+
+require (
+ cel.dev/expr v0.24.0 // indirect
+ github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/blang/semver/v4 v4.0.0 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/emicklei/go-restful/v3 v3.12.2 // indirect
+ github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fsnotify/fsnotify v1.9.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/go-logr/zapr v1.3.0 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/google/btree v1.1.3 // indirect
+ github.com/google/cel-go v0.26.0 // indirect
+ github.com/google/gnostic-models v0.7.0 // indirect
+ github.com/google/go-cmp v0.7.0 // indirect
+ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/prometheus/client_golang v1.22.0 // indirect
+ github.com/prometheus/client_model v0.6.1 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
+ github.com/spf13/cobra v1.9.1 // indirect
+ github.com/spf13/pflag v1.0.6 // indirect
+ github.com/stoewer/go-strcase v1.3.0 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
+ go.opentelemetry.io/otel v1.35.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.35.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.35.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.0 // indirect
+ go.yaml.in/yaml/v2 v2.4.2 // indirect
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
+ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
+ golang.org/x/net v0.38.0 // indirect
+ golang.org/x/oauth2 v0.27.0 // indirect
+ golang.org/x/sync v0.12.0 // indirect
+ golang.org/x/sys v0.31.0 // indirect
+ golang.org/x/term v0.30.0 // indirect
+ golang.org/x/text v0.23.0 // indirect
+ golang.org/x/time v0.9.0 // indirect
+ golang.org/x/tools v0.26.0 // indirect
+ gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
+ google.golang.org/grpc v1.72.1 // indirect
+ google.golang.org/protobuf v1.36.5 // indirect
+ gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ k8s.io/apiextensions-apiserver v0.34.1 // indirect
+ k8s.io/apiserver v0.34.1 // indirect
+ k8s.io/component-base v0.34.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
+ k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
+ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
+ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+ sigs.k8s.io/randfill v1.0.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/go.sum b/docs/book/src/multiversion-tutorial/testdata/project/go.sum
index 20c2a3946ea..0c35e595906 100644
--- a/docs/book/src/multiversion-tutorial/testdata/project/go.sum
+++ b/docs/book/src/multiversion-tutorial/testdata/project/go.sum
@@ -1,640 +1,261 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.51.0 h1:PvKAVQWCtlGUSlZkGW3QLelKaWq7KYv/MW1EboG8bfM=
-cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
-github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
-github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
-github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
+cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
-github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs=
-github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4=
-github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU=
-github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
-github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
-github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
-github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
-github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
-github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
-github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
-github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
-github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
-github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
-github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
+github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
+github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
-github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=
-github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=
-github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
-github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
-github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
+github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8=
-go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
-go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
-go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
-go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
+go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
+go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
+go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
+golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
+golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
-golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
+golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k=
-gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
+gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
+google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
+google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
+google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms=
-k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
-k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA=
-k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg=
-k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
-k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
-k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
-k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc=
-k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
-k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
-k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
-k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
-k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
-k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
-k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
-k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
-k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
-sigs.k8s.io/controller-runtime v0.7.2 h1:gD2JZp0bBLLuvSRYVNvox+bRCz1UUUxKDjPUCb56Ukk=
-sigs.k8s.io/controller-runtime v0.7.2/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
+k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
+k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
+k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
+k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
+k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
+k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
+k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
+k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
+k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
+k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
+k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
+sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
+sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
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 5a8f731859f..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 2021 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.
@@ -12,4 +12,4 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-*/
+*/
\ No newline at end of file
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
new file mode 100644
index 00000000000..091bff8a01c
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller.go
@@ -0,0 +1,718 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+/*
+We'll start out with some imports. You'll see below that we'll need a few more imports
+than those scaffolded for us. We'll talk about each one when we use it.
+*/
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ "sort"
+ "time"
+
+ "github.com/robfig/cron"
+ kbatch "k8s.io/api/batch/v1"
+ corev1 "k8s.io/api/core/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ ref "k8s.io/client-go/tools/reference"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+)
+
+/*
+Next, we'll need a Clock, which will allow us to fake timing in our tests.
+*/
+
+// CronJobReconciler reconciles a CronJob object
+type CronJobReconciler struct {
+ client.Client
+ Scheme *runtime.Scheme
+ Clock
+}
+
+/*
+We'll mock out the clock to make it easier to jump around in time while testing,
+the "real" clock just calls `time.Now`.
+*/
+type realClock struct{}
+
+func (_ realClock) Now() time.Time { return time.Now() } //nolint:staticcheck
+
+// Clock knows how to get the current time.
+// It can be used to fake out timing for testing.
+type Clock interface {
+ Now() time.Time
+}
+
+// +kubebuilder:docs-gen:collapse=Clock Code Implementation
+
+// Definitions to manage status conditions
+const (
+ // typeAvailableCronJob represents the status of the CronJob reconciliation
+ typeAvailableCronJob = "Available"
+ // typeProgressingCronJob represents the status used when the CronJob is being reconciled
+ typeProgressingCronJob = "Progressing"
+ // typeDegradedCronJob represents the status used when the CronJob has encountered an error
+ typeDegradedCronJob = "Degraded"
+)
+
+/*
+Notice that we need a few more RBAC permissions -- since we're creating and
+managing jobs now, we'll need permissions for those, which means adding
+a couple more [markers](/reference/markers/rbac.md).
+*/
+
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=batch.tutorial.kubebuilder.io,resources=cronjobs/finalizers,verbs=update
+// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch,resources=jobs/status,verbs=get
+
+/*
+Now, we get to the heart of the controller -- the reconciler logic.
+*/
+var (
+ scheduledTimeAnnotation = "batch.tutorial.kubebuilder.io/scheduled-at"
+)
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the CronJob object against the actual cluster state, and then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.22.4/pkg/reconcile
+// nolint:gocyclo
+func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ log := logf.FromContext(ctx)
+
+ /*
+ ### 1: Load the CronJob by name
+
+ We'll fetch the CronJob using our client. All client methods take a
+ context (to allow for cancellation) as their first argument, and the object
+ in question as their last. Get is a bit special, in that it takes a
+ [`NamespacedName`](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client?tab=doc#ObjectKey)
+ as the middle argument (most don't have a middle argument, as we'll see
+ below).
+
+ Many client methods also take variadic options at the end.
+ */
+ var cronJob batchv1.CronJob
+ if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
+ if apierrors.IsNotFound(err) {
+ // If the custom resource is not found then it usually means that it was deleted or not created
+ // In this way, we will stop the reconciliation
+ log.Info("CronJob resource not found. Ignoring since object must be deleted")
+ return ctrl.Result{}, nil
+ }
+ // Error reading the object - requeue the request.
+ log.Error(err, "Failed to get CronJob")
+ return ctrl.Result{}, err
+ }
+
+ // Initialize status conditions if not yet present
+ if len(cronJob.Status.Conditions) == 0 {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionUnknown,
+ Reason: "Reconciling",
+ Message: "Starting reconciliation",
+ })
+ if err := r.Status().Update(ctx, &cronJob); err != nil {
+ log.Error(err, "Failed to update CronJob status")
+ return ctrl.Result{}, err
+ }
+
+ // Re-fetch the CronJob after updating the status
+ if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
+ log.Error(err, "Failed to re-fetch CronJob")
+ return ctrl.Result{}, err
+ }
+ }
+
+ /*
+ ### 2: List all active jobs, and update the status
+
+ To fully update our status, we'll need to list all child jobs in this namespace that belong to this CronJob.
+ Similarly to Get, we can use the List method to list the child jobs. Notice that we use variadic options to
+ set the namespace and field match (which is actually an index lookup that we set up below).
+ */
+ var childJobs kbatch.JobList
+ if err := r.List(ctx, &childJobs, client.InNamespace(req.Namespace), client.MatchingFields{jobOwnerKey: req.Name}); err != nil {
+ log.Error(err, "unable to list child Jobs")
+ // Update status condition to reflect the error
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "ReconciliationError",
+ Message: fmt.Sprintf("Failed to list child jobs: %v", err),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ return ctrl.Result{}, err
+ }
+
+ /*
+
+
+
+ What is this index about?
+
+ The reconciler fetches all jobs owned by the cronjob for the status. As our number of cronjobs increases,
+ looking these up can become quite slow as we have to filter through all of them. For a more efficient lookup,
+ these jobs will be indexed locally on the controller's name. A jobOwnerKey field is added to the
+ cached job objects. This key references the owning controller and functions as the index. Later in this
+ document we will configure the manager to actually index this field.
+
+
+
+ Once we have all the jobs we own, we'll split them into active, successful,
+ and failed jobs, keeping track of the most recent run so that we can record it
+ in status. Remember, status should be able to be reconstituted from the state
+ of the world, so it's generally not a good idea to read from the status of the
+ root object. Instead, you should reconstruct it every run. That's what we'll
+ do here.
+
+ We can check if a job is "finished" and whether it succeeded or failed using status
+ conditions. We'll put that logic in a helper to make our code cleaner.
+ */
+
+ // find the active list of jobs
+ var activeJobs []*kbatch.Job
+ var successfulJobs []*kbatch.Job
+ var failedJobs []*kbatch.Job
+ var mostRecentTime *time.Time // find the last run so we can update the status
+
+ /*
+ We consider a job "finished" if it has a "Complete" or "Failed" condition marked as true.
+ Status conditions allow us to add extensible status information to our objects that other
+ humans and controllers can examine to check things like completion and health.
+ */
+ isJobFinished := func(job *kbatch.Job) (bool, kbatch.JobConditionType) {
+ for _, c := range job.Status.Conditions {
+ if (c.Type == kbatch.JobComplete || c.Type == kbatch.JobFailed) && c.Status == corev1.ConditionTrue {
+ return true, c.Type
+ }
+ }
+
+ return false, ""
+ }
+ // +kubebuilder:docs-gen:collapse=isJobFinished
+
+ /*
+ We'll use a helper to extract the scheduled time from the annotation that
+ we added during job creation.
+ */
+ getScheduledTimeForJob := func(job *kbatch.Job) (*time.Time, error) {
+ timeRaw := job.Annotations[scheduledTimeAnnotation]
+ if len(timeRaw) == 0 {
+ return nil, nil
+ }
+
+ timeParsed, err := time.Parse(time.RFC3339, timeRaw)
+ if err != nil {
+ return nil, err
+ }
+ return &timeParsed, nil
+ }
+ // +kubebuilder:docs-gen:collapse=getScheduledTimeForJob
+
+ for i, job := range childJobs.Items {
+ _, finishedType := isJobFinished(&job)
+ switch finishedType {
+ case "": // ongoing
+ activeJobs = append(activeJobs, &childJobs.Items[i])
+ case kbatch.JobFailed:
+ failedJobs = append(failedJobs, &childJobs.Items[i])
+ case kbatch.JobComplete:
+ successfulJobs = append(successfulJobs, &childJobs.Items[i])
+ }
+
+ // We'll store the launch time in an annotation, so we'll reconstitute that from
+ // the active jobs themselves.
+ scheduledTimeForJob, err := getScheduledTimeForJob(&job)
+ if err != nil {
+ log.Error(err, "unable to parse schedule time for child job", "job", &job)
+ continue
+ }
+ if scheduledTimeForJob != nil {
+ if mostRecentTime == nil || mostRecentTime.Before(*scheduledTimeForJob) {
+ mostRecentTime = scheduledTimeForJob
+ }
+ }
+ }
+
+ if mostRecentTime != nil {
+ cronJob.Status.LastScheduleTime = &metav1.Time{Time: *mostRecentTime}
+ } else {
+ cronJob.Status.LastScheduleTime = nil
+ }
+ cronJob.Status.Active = nil
+ for _, activeJob := range activeJobs {
+ jobRef, err := ref.GetReference(r.Scheme, activeJob)
+ if err != nil {
+ log.Error(err, "unable to make reference to active job", "job", activeJob)
+ continue
+ }
+ cronJob.Status.Active = append(cronJob.Status.Active, *jobRef)
+ }
+
+ /*
+ Here, we'll log how many jobs we observed at a slightly higher logging level,
+ for debugging. Notice how instead of using a format string, we use a fixed message,
+ and attach key-value pairs with the extra information. This makes it easier to
+ filter and query log lines.
+ */
+ log.V(1).Info("job count", "active jobs", len(activeJobs), "successful jobs", len(successfulJobs), "failed jobs", len(failedJobs))
+
+ // Check if CronJob is suspended
+ isSuspended := cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend
+
+ // Update status conditions based on current state
+ if isSuspended {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionFalse,
+ Reason: "Suspended",
+ Message: "CronJob is suspended",
+ })
+ } else if len(failedJobs) > 0 {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobsFailed",
+ Message: fmt.Sprintf("%d job(s) have failed", len(failedJobs)),
+ })
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionFalse,
+ Reason: "JobsFailed",
+ Message: fmt.Sprintf("%d job(s) have failed", len(failedJobs)),
+ })
+ } else if len(activeJobs) > 0 {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobsActive",
+ Message: fmt.Sprintf("%d job(s) are currently active", len(activeJobs)),
+ })
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobsActive",
+ Message: fmt.Sprintf("CronJob is progressing with %d active job(s)", len(activeJobs)),
+ })
+ } else {
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeAvailableCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "AllJobsCompleted",
+ Message: "All jobs have completed successfully",
+ })
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionFalse,
+ Reason: "NoJobsActive",
+ Message: "No jobs are currently active",
+ })
+ }
+
+ /*
+ Using the data we've gathered, we'll update the status of our CRD.
+ Just like before, we use our client. To specifically update the status
+ subresource, we'll use the `Status` part of the client, with the `Update`
+ method.
+
+ The status subresource ignores changes to spec, so it's less likely to conflict
+ with any other updates, and can have separate permissions.
+ */
+ if err := r.Status().Update(ctx, &cronJob); err != nil {
+ log.Error(err, "unable to update CronJob status")
+ return ctrl.Result{}, err
+ }
+
+ /*
+ Once we've updated our status, we can move on to ensuring that the status of
+ the world matches what we want in our spec.
+
+ ### 3: Clean up old jobs according to the history limit
+
+ First, we'll try to clean up old jobs, so that we don't leave too many lying
+ around.
+ */
+
+ // NB: deleting these are "best effort" -- if we fail on a particular one,
+ // we won't requeue just to finish the deleting.
+ if cronJob.Spec.FailedJobsHistoryLimit != nil {
+ sort.Slice(failedJobs, func(i, j int) bool {
+ if failedJobs[i].Status.StartTime == nil {
+ return failedJobs[j].Status.StartTime != nil
+ }
+ return failedJobs[i].Status.StartTime.Before(failedJobs[j].Status.StartTime)
+ })
+ for i, job := range failedJobs {
+ if int32(i) >= int32(len(failedJobs))-*cronJob.Spec.FailedJobsHistoryLimit {
+ break
+ }
+ if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
+ log.Error(err, "unable to delete old failed job", "job", job)
+ } else {
+ log.V(0).Info("deleted old failed job", "job", job)
+ }
+ }
+ }
+
+ if cronJob.Spec.SuccessfulJobsHistoryLimit != nil {
+ sort.Slice(successfulJobs, func(i, j int) bool {
+ if successfulJobs[i].Status.StartTime == nil {
+ return successfulJobs[j].Status.StartTime != nil
+ }
+ return successfulJobs[i].Status.StartTime.Before(successfulJobs[j].Status.StartTime)
+ })
+ for i, job := range successfulJobs {
+ if int32(i) >= int32(len(successfulJobs))-*cronJob.Spec.SuccessfulJobsHistoryLimit {
+ break
+ }
+ if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); err != nil {
+ log.Error(err, "unable to delete old successful job", "job", job)
+ } else {
+ log.V(0).Info("deleted old successful job", "job", job)
+ }
+ }
+ }
+
+ /* ### 4: Check if we're suspended
+
+ If this object is suspended, we don't want to run any jobs, so we'll stop now.
+ This is useful if something's broken with the job we're running and we want to
+ pause runs to investigate or putz with the cluster, without deleting the object.
+ */
+
+ if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend {
+ log.V(1).Info("cronjob suspended, skipping")
+ return ctrl.Result{}, nil
+ }
+
+ /*
+ ### 5: Get the next scheduled run
+
+ If we're not paused, we'll need to calculate the next scheduled run, and whether
+ or not we've got a run that we haven't processed yet.
+ */
+
+ /*
+ We'll calculate the next scheduled time using our helpful cron library.
+ We'll start calculating appropriate times from our last run, or the creation
+ of the CronJob if we can't find a last run.
+
+ If there are too many missed runs and we don't have any deadlines set, we'll
+ bail so that we don't cause issues on controller restarts or wedges.
+
+ Otherwise, we'll just return the missed runs (of which we'll just use the latest),
+ and the next run, so that we can know when it's time to reconcile again.
+ */
+ getNextSchedule := func(cronJob *batchv1.CronJob, now time.Time) (lastMissed time.Time, next time.Time, err error) {
+ sched, err := cron.ParseStandard(cronJob.Spec.Schedule)
+ if err != nil {
+ return time.Time{}, time.Time{}, fmt.Errorf("unparseable schedule %q: %w", cronJob.Spec.Schedule, err)
+ }
+
+ // for optimization purposes, cheat a bit and start from our last observed run time
+ // we could reconstitute this here, but there's not much point, since we've
+ // just updated it.
+ var earliestTime time.Time
+ if cronJob.Status.LastScheduleTime != nil {
+ earliestTime = cronJob.Status.LastScheduleTime.Time
+ } else {
+ earliestTime = cronJob.CreationTimestamp.Time
+ }
+ if cronJob.Spec.StartingDeadlineSeconds != nil {
+ // controller is not going to schedule anything below this point
+ schedulingDeadline := now.Add(-time.Second * time.Duration(*cronJob.Spec.StartingDeadlineSeconds))
+
+ if schedulingDeadline.After(earliestTime) {
+ earliestTime = schedulingDeadline
+ }
+ }
+ if earliestTime.After(now) {
+ return time.Time{}, sched.Next(now), nil
+ }
+
+ starts := 0
+ for t := sched.Next(earliestTime); !t.After(now); t = sched.Next(t) {
+ lastMissed = t
+ // An object might miss several starts. For example, if
+ // controller gets wedged on Friday at 5:01pm when everyone has
+ // gone home, and someone comes in on Tuesday AM and discovers
+ // the problem and restarts the controller, then all the hourly
+ // jobs, more than 80 of them for one hourly scheduledJob, should
+ // all start running with no further intervention (if the scheduledJob
+ // allows concurrency and late starts).
+ //
+ // However, if there is a bug somewhere, or incorrect clock
+ // on controller's server or apiservers (for setting creationTimestamp)
+ // then there could be so many missed start times (it could be off
+ // by decades or more), that it would eat up all the CPU and memory
+ // of this controller. In that case, we want to not try to list
+ // all the missed start times.
+ starts++
+ if starts > 100 {
+ // We can't get the most recent times so just return an empty slice
+ return time.Time{}, time.Time{}, fmt.Errorf("Too many missed start times (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.") //nolint:staticcheck
+ }
+ }
+ return lastMissed, sched.Next(now), nil
+ }
+ // +kubebuilder:docs-gen:collapse=getNextSchedule
+
+ // figure out the next times that we need to create
+ // jobs at (or anything we missed).
+ missedRun, nextRun, err := getNextSchedule(&cronJob, r.Now())
+ if err != nil {
+ log.Error(err, "unable to figure out CronJob schedule")
+ // Update status condition to reflect the schedule error
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "InvalidSchedule",
+ Message: fmt.Sprintf("Failed to parse schedule: %v", err),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ // we don't really care about requeuing until we get an update that
+ // fixes the schedule, so don't return an error
+ return ctrl.Result{}, nil
+ }
+
+ /*
+ We'll prep our eventual request to requeue until the next job, and then figure
+ out if we actually need to run.
+ */
+ scheduledResult := ctrl.Result{RequeueAfter: nextRun.Sub(r.Now())} // save this so we can re-use it elsewhere
+ log = log.WithValues("now", r.Now(), "next run", nextRun)
+
+ /*
+ ### 6: Run a new job if it's on schedule, not past the deadline, and not blocked by our concurrency policy
+
+ If we've missed a run, and we're still within the deadline to start it, we'll need to run a job.
+ */
+ if missedRun.IsZero() {
+ log.V(1).Info("no upcoming scheduled times, sleeping until next")
+ return scheduledResult, nil
+ }
+
+ // make sure we're not too late to start the run
+ log = log.WithValues("current run", missedRun)
+ tooLate := false
+ if cronJob.Spec.StartingDeadlineSeconds != nil {
+ tooLate = missedRun.Add(time.Duration(*cronJob.Spec.StartingDeadlineSeconds) * time.Second).Before(r.Now())
+ }
+ if tooLate {
+ log.V(1).Info("missed starting deadline for last run, sleeping till next")
+ // Update status condition to reflect missed deadline
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "MissedSchedule",
+ Message: fmt.Sprintf("Missed starting deadline for run at %v", missedRun),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ return scheduledResult, nil
+ }
+
+ /*
+ If we actually have to run a job, we'll need to either wait till existing ones finish,
+ replace the existing ones, or just add new ones. If our information is out of date due
+ to cache delay, we'll get a requeue when we get up-to-date information.
+ */
+ // figure out how to run this job -- concurrency policy might forbid us from running
+ // multiple at the same time...
+ if cronJob.Spec.ConcurrencyPolicy == batchv1.ForbidConcurrent && len(activeJobs) > 0 {
+ log.V(1).Info("concurrency policy blocks concurrent runs, skipping", "num active", len(activeJobs))
+ return scheduledResult, nil
+ }
+
+ // ...or instruct us to replace existing ones...
+ if cronJob.Spec.ConcurrencyPolicy == batchv1.ReplaceConcurrent {
+ for _, activeJob := range activeJobs {
+ // we don't care if the job was already deleted
+ if err := r.Delete(ctx, activeJob, client.PropagationPolicy(metav1.DeletePropagationBackground)); client.IgnoreNotFound(err) != nil {
+ log.Error(err, "unable to delete active job", "job", activeJob)
+ return ctrl.Result{}, err
+ }
+ }
+ }
+
+ /*
+ Once we've figured out what to do with existing jobs, we'll actually create our desired job
+ */
+
+ /*
+ We need to construct a job based on our CronJob's template. We'll copy over the spec
+ from the template and copy some basic object meta.
+
+ Then, we'll set the "scheduled time" annotation so that we can reconstitute our
+ `LastScheduleTime` field each reconcile.
+
+ Finally, we'll need to set an owner reference. This allows the Kubernetes garbage collector
+ to clean up jobs when we delete the CronJob, and allows controller-runtime to figure out
+ which cronjob needs to be reconciled when a given job changes (is added, deleted, completes, etc).
+ */
+ constructJobForCronJob := func(cronJob *batchv1.CronJob, scheduledTime time.Time) (*kbatch.Job, error) {
+ // We want job names for a given nominal start time to have a deterministic name to avoid the same job being created twice
+ name := fmt.Sprintf("%s-%d", cronJob.Name, scheduledTime.Unix())
+
+ job := &kbatch.Job{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: make(map[string]string),
+ Annotations: make(map[string]string),
+ Name: name,
+ Namespace: cronJob.Namespace,
+ },
+ Spec: *cronJob.Spec.JobTemplate.Spec.DeepCopy(),
+ }
+ for k, v := range cronJob.Spec.JobTemplate.Annotations {
+ job.Annotations[k] = v
+ }
+ job.Annotations[scheduledTimeAnnotation] = scheduledTime.Format(time.RFC3339)
+ for k, v := range cronJob.Spec.JobTemplate.Labels {
+ job.Labels[k] = v
+ }
+ if err := ctrl.SetControllerReference(cronJob, job, r.Scheme); err != nil {
+ return nil, err
+ }
+
+ return job, nil
+ }
+ // +kubebuilder:docs-gen:collapse=constructJobForCronJob
+
+ // actually make the job...
+ job, err := constructJobForCronJob(&cronJob, missedRun)
+ if err != nil {
+ log.Error(err, "unable to construct job from template")
+ // don't bother requeuing until we get a change to the spec
+ return scheduledResult, nil
+ }
+
+ // ...and create it on the cluster
+ if err := r.Create(ctx, job); err != nil {
+ log.Error(err, "unable to create Job for CronJob", "job", job)
+ // Update status condition to reflect the error
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeDegradedCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobCreationFailed",
+ Message: fmt.Sprintf("Failed to create job: %v", err),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+ return ctrl.Result{}, err
+ }
+
+ log.V(1).Info("created Job for CronJob run", "job", job)
+
+ // Update status condition to reflect successful job creation
+ meta.SetStatusCondition(&cronJob.Status.Conditions, metav1.Condition{
+ Type: typeProgressingCronJob,
+ Status: metav1.ConditionTrue,
+ Reason: "JobCreated",
+ Message: fmt.Sprintf("Created job %s", job.Name),
+ })
+ if statusErr := r.Status().Update(ctx, &cronJob); statusErr != nil {
+ log.Error(statusErr, "Failed to update CronJob status")
+ }
+
+ /*
+ ### 7: Requeue when we either see a running job or it's time for the next scheduled run
+
+ Finally, we'll return the result that we prepped above, that says we want to requeue
+ when our next run would need to occur. This is taken as a maximum deadline -- if something
+ else changes in between, like our job starts or finishes, we get modified, etc, we might
+ reconcile again sooner.
+ */
+ // we'll requeue once we see the running job, and update our status
+ return scheduledResult, nil
+}
+
+/*
+### Setup
+
+Finally, we'll update our setup. In order to allow our reconciler to quickly
+look up Jobs by their owner, we'll need an index. We declare an index key that
+we can later use with the client as a pseudo-field name, and then describe how to
+extract the indexed value from the Job object. The indexer will automatically take
+care of namespaces for us, so we just have to extract the owner name if the Job has
+a CronJob owner.
+
+Additionally, we'll inform the manager that this controller owns some Jobs, so that it
+will automatically call Reconcile on the underlying CronJob when a Job changes, is
+deleted, etc.
+*/
+var (
+ jobOwnerKey = ".metadata.controller"
+ apiGVStr = batchv1.GroupVersion.String()
+)
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *CronJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ // set up a real clock, since we're not in a test
+ if r.Clock == nil {
+ r.Clock = realClock{}
+ }
+
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &kbatch.Job{}, jobOwnerKey, func(rawObj client.Object) []string {
+ // grab the job object, extract the owner...
+ job := rawObj.(*kbatch.Job)
+ owner := metav1.GetControllerOf(job)
+ if owner == nil {
+ return nil
+ }
+ // ...make sure it's a CronJob...
+ if owner.APIVersion != apiGVStr || owner.Kind != "CronJob" {
+ return nil
+ }
+
+ // ...and if so, return it
+ return []string{owner.Name}
+ }); err != nil {
+ return err
+ }
+
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&batchv1.CronJob{}).
+ Owns(&kbatch.Job{}).
+ Named("cronjob").
+ Complete(r)
+}
diff --git a/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller_test.go b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller_test.go
new file mode 100644
index 00000000000..a6fbe2d1fe7
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/cronjob_controller_test.go
@@ -0,0 +1,215 @@
+/*
+
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+/*
+Ideally, we should have one `_controller_test.go` for each controller scaffolded and called in the `suite_test.go`.
+So, let's write our example test for the CronJob controller (`cronjob_controller_test.go.`)
+*/
+
+/*
+As usual, we start with the necessary imports. We also define some utility variables.
+*/
+package controller
+
+import (
+ "context"
+ "reflect"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ batchv1 "k8s.io/api/batch/v1"
+ v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+
+ cronjobv1 "tutorial.kubebuilder.io/project/api/v1"
+)
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+// Helper function to check if a specific condition exists with expected status
+func hasCondition(conditions []metav1.Condition, conditionType string, expectedStatus metav1.ConditionStatus) bool {
+ for _, condition := range conditions {
+ if condition.Type == conditionType && condition.Status == expectedStatus {
+ return true
+ }
+ }
+ return false
+}
+
+/*
+The first step to writing a simple integration test is to actually create an instance of CronJob you can run tests against.
+Note that to create a CronJob, you’ll need to create a stub CronJob struct that contains your CronJob’s specifications.
+
+Note that when we create a stub CronJob, the CronJob also needs stubs of its required downstream objects.
+Without the stubbed Job template spec and the Pod template spec below, the Kubernetes API will not be able to
+create the CronJob.
+*/
+var _ = Describe("CronJob controller", func() {
+
+ // Define utility constants for object names and testing timeouts/durations and intervals.
+ const (
+ CronjobName = "test-cronjob"
+ CronjobNamespace = "default"
+ JobName = "test-job"
+
+ timeout = time.Second * 10
+ duration = time.Second * 10
+ interval = time.Millisecond * 250
+ )
+
+ Context("When updating CronJob Status", func() {
+ It("Should increase CronJob Status.Active count when new Jobs are created", func() {
+ By("By creating a new CronJob")
+ ctx := context.Background()
+ cronJob := &cronjobv1.CronJob{
+ TypeMeta: metav1.TypeMeta{
+ APIVersion: "batch.tutorial.kubebuilder.io/v1",
+ Kind: "CronJob",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: CronjobName,
+ Namespace: CronjobNamespace,
+ },
+ Spec: cronjobv1.CronJobSpec{
+ Schedule: "1 * * * *",
+ JobTemplate: batchv1.JobTemplateSpec{
+ Spec: batchv1.JobSpec{
+ // For simplicity, we only fill out the required fields.
+ Template: v1.PodTemplateSpec{
+ Spec: v1.PodSpec{
+ // For simplicity, we only fill out the required fields.
+ Containers: []v1.Container{
+ {
+ Name: "test-container",
+ Image: "test-image",
+ },
+ },
+ RestartPolicy: v1.RestartPolicyOnFailure,
+ },
+ },
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Create(ctx, cronJob)).To(Succeed())
+
+ /*
+ After creating this CronJob, let's check that the CronJob's Spec fields match what we passed in.
+ Note that, because the k8s apiserver may not have finished creating a CronJob after our `Create()` call from earlier, we will use Gomega’s Eventually() testing function instead of Expect() to give the apiserver an opportunity to finish creating our CronJob.
+
+ `Eventually()` will repeatedly run the function provided as an argument every interval seconds until
+ (a) the assertions done by the passed-in `Gomega` succeed, or
+ (b) the number of attempts * interval period exceed the provided timeout value.
+
+ In the examples below, timeout and interval are Go Duration values of our choosing.
+ */
+
+ cronjobLookupKey := types.NamespacedName{Name: CronjobName, Namespace: CronjobNamespace}
+ createdCronjob := &cronjobv1.CronJob{}
+
+ // We'll need to retry getting this newly created CronJob, given that creation may not immediately happen.
+ Eventually(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
+ }, timeout, interval).Should(Succeed())
+ // Let's make sure our Schedule string value was properly converted/handled.
+ Expect(createdCronjob.Spec.Schedule).To(Equal("1 * * * *"))
+ /*
+ Now that we've created a CronJob in our test cluster, the next step is to write a test that actually tests our CronJob controller’s behavior.
+ Let’s test the CronJob controller’s logic responsible for updating CronJob.Status.Active with actively running jobs.
+ We’ll verify that when a CronJob has a single active downstream Job, its CronJob.Status.Active field contains a reference to this Job.
+
+ First, we should get the test CronJob we created earlier, and verify that it currently does not have any active jobs.
+ We use Gomega's `Consistently()` check here to ensure that the active job count remains 0 over a duration of time.
+ */
+ By("By checking the CronJob has zero active Jobs")
+ Consistently(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
+ g.Expect(createdCronjob.Status.Active).To(BeEmpty())
+ }, duration, interval).Should(Succeed())
+ /*
+ Next, we actually create a stubbed Job that will belong to our CronJob, as well as its downstream template specs.
+ We set the Job's status's "Active" count to 2 to simulate the Job running two pods, which means the Job is actively running.
+
+ We then take the stubbed Job and set its owner reference to point to our test CronJob.
+ This ensures that the test Job belongs to, and is tracked by, our test CronJob.
+ Once that’s done, we create our new Job instance.
+ */
+ By("By creating a new Job")
+ testJob := &batchv1.Job{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: JobName,
+ Namespace: CronjobNamespace,
+ },
+ Spec: batchv1.JobSpec{
+ Template: v1.PodTemplateSpec{
+ Spec: v1.PodSpec{
+ // For simplicity, we only fill out the required fields.
+ Containers: []v1.Container{
+ {
+ Name: "test-container",
+ Image: "test-image",
+ },
+ },
+ RestartPolicy: v1.RestartPolicyOnFailure,
+ },
+ },
+ },
+ }
+
+ // Note that your CronJob’s GroupVersionKind is required to set up this owner reference.
+ kind := reflect.TypeOf(cronjobv1.CronJob{}).Name()
+ gvk := cronjobv1.GroupVersion.WithKind(kind)
+
+ controllerRef := metav1.NewControllerRef(createdCronjob, gvk)
+ testJob.SetOwnerReferences([]metav1.OwnerReference{*controllerRef})
+ Expect(k8sClient.Create(ctx, testJob)).To(Succeed())
+ // Note that you can not manage the status values while creating the resource.
+ // The status field is managed separately to reflect the current state of the resource.
+ // Therefore, it should be updated using a PATCH or PUT operation after the resource has been created.
+ // Additionally, it is recommended to use StatusConditions to manage the status. For further information see:
+ // https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
+ testJob.Status.Active = 2
+ Expect(k8sClient.Status().Update(ctx, testJob)).To(Succeed())
+ /*
+ Adding this Job to our test CronJob should trigger our controller’s reconciler logic.
+ After that, we can write a test that evaluates whether our controller eventually updates our CronJob’s Status field as expected!
+ */
+ By("By checking that the CronJob has one active Job")
+ Eventually(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed(), "should GET the CronJob")
+ g.Expect(createdCronjob.Status.Active).To(HaveLen(1), "should have exactly one active job")
+ g.Expect(createdCronjob.Status.Active[0].Name).To(Equal(JobName), "the wrong job is active")
+ }, timeout, interval).Should(Succeed(), "should list our active job %s in the active jobs list in status", JobName)
+
+ By("By checking that the CronJob status conditions are properly set")
+ Eventually(func(g Gomega) {
+ g.Expect(k8sClient.Get(ctx, cronjobLookupKey, createdCronjob)).To(Succeed())
+ // Check that the Available condition is set to True when job is active
+ g.Expect(hasCondition(createdCronjob.Status.Conditions, "Available", metav1.ConditionTrue)).To(BeTrue(),
+ "CronJob should have Available condition set to True")
+ }, timeout, interval).Should(Succeed())
+ })
+ })
+
+})
+
+// +kubebuilder:docs-gen:collapse=Remaining code from cronjob_controller_test.go
+
+/*
+ After writing all this code, you can run `go test ./...` in your `controllers/` directory again to run your new test!
+*/
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
new file mode 100644
index 00000000000..c74dd849aac
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/controller/suite_test.go
@@ -0,0 +1,200 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+/*
+When we created the CronJob API with `kubebuilder create api` in a [previous chapter](/cronjob-tutorial/new-api.md), Kubebuilder already did some test work for you.
+Kubebuilder scaffolded a `internal/controller/suite_test.go` file that does the bare bones of setting up a test environment.
+
+First, it will contain the necessary imports.
+*/
+
+package controller
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+ "testing"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+ 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.
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+/*
+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.
+)
+
+func TestControllers(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Controller Suite")
+}
+
+var _ = BeforeSuite(func() {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+
+ 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
+
+ /*
+ 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,
+ }
+
+ // Retrieve the first found binary directory to allow running tests from IDEs
+ if getFirstFoundEnvTestBinaryDir() != "" {
+ testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir()
+ }
+ /*
+ Then, we start the envtest cluster.
+ */
+
+ // cfg is defined in this file globally.
+ cfg, err = testEnv.Start()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(cfg).NotTo(BeNil())
+
+ /*
+ A client is created for our test CRUD operations.
+ */
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ /*
+ One thing that this autogenerated file is missing, however, is a way to actually start your controller.
+ The code above will set up a client for interacting with your custom Kind,
+ but will not be able to test your controller behavior.
+ If you want to test your custom controller logic, you’ll need to add some familiar-looking manager logic
+ to your BeforeSuite() function, so you can register your custom controller to run on this test cluster.
+
+ You may notice that the code below runs your controller with nearly identical logic to your CronJob project’s main.go!
+ The only difference is that the manager is started in a separate goroutine so it does not block the cleanup of envtest
+ when you’re done running your tests.
+
+ Note that we set up both a "live" k8s client and a separate client from the manager. This is because when making
+ assertions in tests, you generally want to assert against the live state of the API server. If you use the client
+ from the manager (`k8sManager.GetClient`), you'd end up asserting against the contents of the cache instead, which is
+ slower and can introduce flakiness into your tests. We could use the manager's `APIReader` to accomplish the same
+ thing, but that would leave us with two clients in our test assertions and setup (one for reading, one for writing),
+ and it'd be easy to make mistakes.
+
+ Note that we keep the reconciler running against the manager's cache client, though -- we want our controller to
+ behave as it would in production, and we use features of the cache (like indices) in our controller which aren't
+ available when talking directly to the API server.
+ */
+ k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&CronJobReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ go func() {
+ defer GinkgoRecover()
+ err = k8sManager.Start(ctx)
+ Expect(err).ToNot(HaveOccurred(), "failed to run manager")
+ }()
+})
+
+/*
+Kubebuilder also generates boilerplate functions for cleaning up envtest and actually running your test files in your controllers/ directory.
+You won't need to touch these.
+*/
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ cancel()
+ err := testEnv.Stop()
+ Expect(err).NotTo(HaveOccurred())
+})
+
+// +kubebuilder:docs-gen:collapse=Remaining code from suite_test.go
+
+/*
+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
new file mode 100644
index 00000000000..55a53644ccc
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook.go
@@ -0,0 +1,285 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+package v1
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/robfig/cron"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ validationutils "k8s.io/apimachinery/pkg/util/validation"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+)
+
+// +kubebuilder:docs-gen:collapse=Imports
+
+/*
+Next, we'll setup a logger for the webhooks.
+*/
+
+var cronjoblog = logf.Log.WithName("cronjob-resource")
+
+/*
+This setup doubles as setup for our conversion webhooks: as long as our
+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.
+func SetupCronJobWebhookWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).For(&batchv1.CronJob{}).
+ WithValidator(&CronJobCustomValidator{}).
+ WithDefaulter(&CronJobCustomDefaulter{
+ DefaultConcurrencyPolicy: batchv1.AllowConcurrent,
+ DefaultSuspend: false,
+ DefaultSuccessfulJobsHistoryLimit: 3,
+ DefaultFailedJobsHistoryLimit: 1,
+ }).
+ Complete()
+}
+
+/*
+Notice that we use kubebuilder markers to generate webhook manifests.
+This marker is responsible for generating a mutating webhook manifest.
+
+The meaning of each marker can be found [here](/reference/markers/webhook.md).
+*/
+
+/*
+This marker is responsible for generating a mutation webhook manifest.
+*/
+
+// +kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=true,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=mcronjob-v1.kb.io,admissionReviewVersions=v1
+
+// CronJobCustomDefaulter struct is responsible for setting default values on the custom resource of the
+// Kind CronJob when those are created or updated.
+//
+// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
+// as it is used only for temporary operations and does not need to be deeply copied.
+type CronJobCustomDefaulter struct {
+
+ // Default values for various CronJob fields
+ DefaultConcurrencyPolicy batchv1.ConcurrencyPolicy
+ DefaultSuspend bool
+ DefaultSuccessfulJobsHistoryLimit int32
+ DefaultFailedJobsHistoryLimit int32
+}
+
+var _ webhook.CustomDefaulter = &CronJobCustomDefaulter{}
+
+/*
+We use the `webhook.CustomDefaulter`interface to set defaults to our CRD.
+A webhook will automatically be served that calls this defaulting.
+
+The `Default`method is expected to mutate the receiver, setting the defaults.
+*/
+
+// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind CronJob.
+func (d *CronJobCustomDefaulter) Default(_ context.Context, obj runtime.Object) error {
+ cronjob, ok := obj.(*batchv1.CronJob)
+
+ if !ok {
+ return fmt.Errorf("expected an CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Defaulting for CronJob", "name", cronjob.GetName())
+
+ // Set default values
+ d.applyDefaults(cronjob)
+ return nil
+}
+
+// applyDefaults applies default values to CronJob fields.
+func (d *CronJobCustomDefaulter) applyDefaults(cronJob *batchv1.CronJob) {
+ if cronJob.Spec.ConcurrencyPolicy == "" {
+ cronJob.Spec.ConcurrencyPolicy = d.DefaultConcurrencyPolicy
+ }
+ if cronJob.Spec.Suspend == nil {
+ cronJob.Spec.Suspend = new(bool)
+ *cronJob.Spec.Suspend = d.DefaultSuspend
+ }
+ if cronJob.Spec.SuccessfulJobsHistoryLimit == nil {
+ cronJob.Spec.SuccessfulJobsHistoryLimit = new(int32)
+ *cronJob.Spec.SuccessfulJobsHistoryLimit = d.DefaultSuccessfulJobsHistoryLimit
+ }
+ if cronJob.Spec.FailedJobsHistoryLimit == nil {
+ cronJob.Spec.FailedJobsHistoryLimit = new(int32)
+ *cronJob.Spec.FailedJobsHistoryLimit = d.DefaultFailedJobsHistoryLimit
+ }
+}
+
+/*
+We can validate our CRD beyond what's possible with declarative
+validation. Generally, declarative validation should be sufficient, but
+sometimes more advanced use cases call for complex validation.
+
+For instance, we'll see below that we use this to validate a well-formed cron
+schedule without making up a long regular expression.
+
+If `webhook.CustomValidator` interface is implemented, a webhook will automatically be
+served that calls the validation.
+
+The `ValidateCreate`, `ValidateUpdate` and `ValidateDelete` methods are expected
+to validate its receiver upon creation, update and deletion respectively.
+We separate out ValidateCreate from ValidateUpdate to allow behavior like making
+certain fields immutable, so that they can only be set on creation.
+ValidateDelete is also separated from ValidateUpdate to allow different
+validation behavior on deletion.
+Here, however, we just use the same shared validation for `ValidateCreate` and
+`ValidateUpdate`. And we do nothing in `ValidateDelete`, since we don't need to
+validate anything on deletion.
+*/
+
+/*
+This marker is responsible for generating a validation webhook manifest.
+*/
+
+// NOTE: If you want to customise the 'path', use the flags '--defaulting-path' or '--validation-path'.
+// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v1,name=vcronjob-v1.kb.io,admissionReviewVersions=v1
+
+// CronJobCustomValidator struct is responsible for validating the CronJob resource
+// when it is created, updated, or deleted.
+//
+// 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 { // +kubebuilder:docs-gen:collapse=Remaining Webhook Code
+ // TODO(user): Add more fields as needed for validation
+}
+
+var _ webhook.CustomValidator = &CronJobCustomValidator{}
+
+// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := obj.(*batchv1.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Validation for CronJob upon creation", "name", cronjob.GetName())
+
+ return nil, validateCronJob(cronjob)
+}
+
+// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := newObj.(*batchv1.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object for the newObj but got %T", newObj)
+ }
+ cronjoblog.Info("Validation for CronJob upon update", "name", cronjob.GetName())
+
+ return nil, validateCronJob(cronjob)
+}
+
+// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := obj.(*batchv1.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Validation for CronJob upon deletion", "name", cronjob.GetName())
+
+ // TODO(user): fill in your validation logic upon object deletion.
+
+ return nil, nil
+}
+
+/*
+We validate the name and the spec of the CronJob.
+*/
+
+// validateCronJob validates the fields of a CronJob object.
+func validateCronJob(cronjob *batchv1.CronJob) error {
+ var allErrs field.ErrorList
+ if err := validateCronJobName(cronjob); err != nil {
+ allErrs = append(allErrs, err)
+ }
+ if err := validateCronJobSpec(cronjob); err != nil {
+ allErrs = append(allErrs, err)
+ }
+ if len(allErrs) == 0 {
+ return nil
+ }
+
+ return apierrors.NewInvalid(
+ schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"},
+ cronjob.Name, allErrs)
+}
+
+/*
+Some fields are declaratively validated by OpenAPI schema.
+You can find kubebuilder validation markers (prefixed
+with `// +kubebuilder:validation`) in the
+[Designing an API](api-design.md) section.
+You can find all of the kubebuilder supported markers for
+declaring validation by running `controller-gen crd -w`,
+or [here](/reference/markers/crd-validation.md).
+*/
+
+func validateCronJobSpec(cronjob *batchv1.CronJob) *field.Error {
+ // The field helpers from the kubernetes API machinery help us return nicely
+ // structured validation errors.
+ return validateScheduleFormat(
+ cronjob.Spec.Schedule,
+ field.NewPath("spec").Child("schedule"))
+}
+
+/*
+We'll need to validate the [cron](https://en.wikipedia.org/wiki/Cron) schedule
+is well-formatted.
+*/
+
+func validateScheduleFormat(schedule string, fldPath *field.Path) *field.Error {
+ if _, err := cron.ParseStandard(schedule); err != nil {
+ return field.Invalid(fldPath, schedule, err.Error())
+ }
+ return nil
+}
+
+/*
+Validating the length of a string field can be done declaratively by
+the validation schema.
+
+But the `ObjectMeta.Name` field is defined in a shared package under
+the apimachinery repo, so we can't declaratively validate it using
+the validation schema.
+*/
+
+func validateCronJobName(cronjob *batchv1.CronJob) *field.Error {
+ if len(cronjob.Name) > validationutils.DNS1035LabelMaxLength-11 {
+ // The job name length is 63 characters like all Kubernetes objects
+ // (which must fit in a DNS subdomain). The cronjob controller appends
+ // a 11-character suffix to the cronjob (`-$TIMESTAMP`) when creating
+ // a job. The job name length limit is 63 characters. Therefore cronjob
+ // names must have length <= 63-11=52. If we don't validate this here,
+ // then job creation will fail later.
+ return field.Invalid(field.NewPath("metadata").Child("name"), cronjob.Name, "must be no more than 52 characters")
+ }
+ return nil
+}
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
new file mode 100644
index 00000000000..c7983a21a63
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/cronjob_webhook_test.go
@@ -0,0 +1,180 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1
+
+import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ batchv1 "tutorial.kubebuilder.io/project/api/v1"
+ // TODO (user): Add any additional imports if needed
+ "k8s.io/utils/ptr"
+)
+
+var _ = Describe("CronJob Webhook", func() {
+ var (
+ obj *batchv1.CronJob
+ oldObj *batchv1.CronJob
+ validator CronJobCustomValidator
+ defaulter CronJobCustomDefaulter
+ )
+ const validCronJobName = "valid-cronjob-name"
+ const schedule = "*/5 * * * *"
+
+ BeforeEach(func() {
+ obj = &batchv1.CronJob{
+ Spec: batchv1.CronJobSpec{
+ Schedule: schedule,
+ ConcurrencyPolicy: batchv1.AllowConcurrent,
+ SuccessfulJobsHistoryLimit: ptr.To(int32(3)),
+ FailedJobsHistoryLimit: ptr.To(int32(1)),
+ },
+ }
+ *obj.Spec.SuccessfulJobsHistoryLimit = 3
+ *obj.Spec.FailedJobsHistoryLimit = 1
+
+ oldObj = &batchv1.CronJob{
+ Spec: batchv1.CronJobSpec{
+ Schedule: schedule,
+ ConcurrencyPolicy: batchv1.AllowConcurrent,
+ SuccessfulJobsHistoryLimit: ptr.To(int32(3)),
+ FailedJobsHistoryLimit: ptr.To(int32(1)),
+ },
+ }
+ *oldObj.Spec.SuccessfulJobsHistoryLimit = 3
+ *oldObj.Spec.FailedJobsHistoryLimit = 1
+
+ validator = CronJobCustomValidator{}
+ defaulter = CronJobCustomDefaulter{
+ DefaultConcurrencyPolicy: batchv1.AllowConcurrent,
+ DefaultSuspend: false,
+ DefaultSuccessfulJobsHistoryLimit: 3,
+ DefaultFailedJobsHistoryLimit: 1,
+ }
+
+ Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
+ Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
+ })
+
+ AfterEach(func() {
+ // TODO (user): Add any teardown logic common to all tests
+ })
+
+ Context("When creating CronJob under Defaulting Webhook", func() {
+ It("Should apply defaults when a required field is empty", func() {
+ By("simulating a scenario where defaults should be applied")
+ obj.Spec.ConcurrencyPolicy = "" // This should default to AllowConcurrent
+ obj.Spec.Suspend = nil // This should default to false
+ obj.Spec.SuccessfulJobsHistoryLimit = nil // This should default to 3
+ obj.Spec.FailedJobsHistoryLimit = nil // This should default to 1
+
+ By("calling the Default method to apply defaults")
+ _ = defaulter.Default(ctx, obj)
+
+ By("checking that the default values are set")
+ Expect(obj.Spec.ConcurrencyPolicy).To(Equal(batchv1.AllowConcurrent), "Expected ConcurrencyPolicy to default to AllowConcurrent")
+ Expect(*obj.Spec.Suspend).To(BeFalse(), "Expected Suspend to default to false")
+ Expect(*obj.Spec.SuccessfulJobsHistoryLimit).To(Equal(int32(3)), "Expected SuccessfulJobsHistoryLimit to default to 3")
+ Expect(*obj.Spec.FailedJobsHistoryLimit).To(Equal(int32(1)), "Expected FailedJobsHistoryLimit to default to 1")
+ })
+
+ It("Should not overwrite fields that are already set", func() {
+ By("setting fields that would normally get a default")
+ obj.Spec.ConcurrencyPolicy = batchv1.ForbidConcurrent
+ obj.Spec.Suspend = new(bool)
+ *obj.Spec.Suspend = true
+ obj.Spec.SuccessfulJobsHistoryLimit = new(int32)
+ *obj.Spec.SuccessfulJobsHistoryLimit = 5
+ obj.Spec.FailedJobsHistoryLimit = new(int32)
+ *obj.Spec.FailedJobsHistoryLimit = 2
+
+ By("calling the Default method to apply defaults")
+ _ = defaulter.Default(ctx, obj)
+
+ By("checking that the fields were not overwritten")
+ Expect(obj.Spec.ConcurrencyPolicy).To(Equal(batchv1.ForbidConcurrent), "Expected ConcurrencyPolicy to retain its set value")
+ Expect(*obj.Spec.Suspend).To(BeTrue(), "Expected Suspend to retain its set value")
+ Expect(*obj.Spec.SuccessfulJobsHistoryLimit).To(Equal(int32(5)), "Expected SuccessfulJobsHistoryLimit to retain its set value")
+ Expect(*obj.Spec.FailedJobsHistoryLimit).To(Equal(int32(2)), "Expected FailedJobsHistoryLimit to retain its set value")
+ })
+ })
+
+ Context("When creating or updating CronJob under Validating Webhook", func() {
+ It("Should deny creation if the name is too long", func() {
+ obj.Name = "this-name-is-way-too-long-and-should-fail-validation-because-it-is-way-too-long"
+ Expect(validator.ValidateCreate(ctx, obj)).Error().To(
+ MatchError(ContainSubstring("must be no more than 52 characters")),
+ "Expected name validation to fail for a too-long name")
+ })
+
+ It("Should admit creation if the name is valid", func() {
+ obj.Name = validCronJobName
+ Expect(validator.ValidateCreate(ctx, obj)).To(BeNil(),
+ "Expected name validation to pass for a valid name")
+ })
+
+ It("Should deny creation if the schedule is invalid", func() {
+ obj.Spec.Schedule = "invalid-cron-schedule"
+ Expect(validator.ValidateCreate(ctx, obj)).Error().To(
+ MatchError(ContainSubstring("Expected exactly 5 fields, found 1: invalid-cron-schedule")),
+ "Expected spec validation to fail for an invalid schedule")
+ })
+
+ It("Should admit creation if the schedule is valid", func() {
+ obj.Spec.Schedule = schedule
+ Expect(validator.ValidateCreate(ctx, obj)).To(BeNil(),
+ "Expected spec validation to pass for a valid schedule")
+ })
+
+ It("Should deny update if both name and spec are invalid", func() {
+ oldObj.Name = validCronJobName
+ oldObj.Spec.Schedule = schedule
+
+ By("simulating an update")
+ obj.Name = "this-name-is-way-too-long-and-should-fail-validation-because-it-is-way-too-long"
+ obj.Spec.Schedule = "invalid-cron-schedule"
+
+ By("validating an update")
+ Expect(validator.ValidateUpdate(ctx, oldObj, obj)).Error().To(HaveOccurred(),
+ "Expected validation to fail for both name and spec")
+ })
+
+ It("Should admit update if both name and spec are valid", func() {
+ oldObj.Name = validCronJobName
+ oldObj.Spec.Schedule = schedule
+
+ By("simulating an update")
+ obj.Name = "valid-cronjob-name-updated"
+ obj.Spec.Schedule = "0 0 * * *"
+
+ By("validating an update")
+ Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil(),
+ "Expected validation to pass for a valid update")
+ })
+ })
+
+ 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
new file mode 100644
index 00000000000..e286589ed57
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v1/webhook_suite_test.go
@@ -0,0 +1,164 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "net"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "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 (
+ ctx context.Context
+ cancel context.CancelFunc
+ k8sClient client.Client
+ cfg *rest.Config
+ testEnv *envtest.Environment
+)
+
+func TestAPIs(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Webhook Suite")
+}
+
+var _ = BeforeSuite(func() {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+
+ ctx, cancel = context.WithCancel(context.TODO())
+
+ var err error
+ err = batchv1.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: false,
+
+ WebhookInstallOptions: envtest.WebhookInstallOptions{
+ Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
+ },
+ }
+
+ // 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())
+
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ // start webhook server using Manager.
+ webhookInstallOptions := &testEnv.WebhookInstallOptions
+ mgr, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ WebhookServer: webhook.NewServer(webhook.Options{
+ Host: webhookInstallOptions.LocalServingHost,
+ Port: webhookInstallOptions.LocalServingPort,
+ CertDir: webhookInstallOptions.LocalServingCertDir,
+ }),
+ LeaderElection: false,
+ Metrics: metricsserver.Options{BindAddress: "0"},
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ err = SetupCronJobWebhookWithManager(mgr)
+ Expect(err).NotTo(HaveOccurred())
+
+ // +kubebuilder:scaffold:webhook
+
+ go func() {
+ defer GinkgoRecover()
+ err = mgr.Start(ctx)
+ Expect(err).NotTo(HaveOccurred())
+ }()
+
+ // wait for the webhook server to get ready.
+ dialer := &net.Dialer{Timeout: time.Second}
+ addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
+ Eventually(func() error {
+ conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
+ if err != nil {
+ return err
+ }
+
+ return conn.Close()
+ }).Should(Succeed())
+})
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ cancel()
+ 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
new file mode 100644
index 00000000000..82668225bc6
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook.go
@@ -0,0 +1,215 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// +kubebuilder:docs-gen:collapse=Apache License
+
+package v2
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/robfig/cron"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ validationutils "k8s.io/apimachinery/pkg/util/validation"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ ctrl "sigs.k8s.io/controller-runtime"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/webhook"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ batchv2 "tutorial.kubebuilder.io/project/api/v2"
+)
+
+// nolint:unused
+// log is for logging in this package.
+var cronjoblog = logf.Log.WithName("cronjob-resource")
+
+// SetupCronJobWebhookWithManager registers the webhook for CronJob in the manager.
+func SetupCronJobWebhookWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewWebhookManagedBy(mgr).For(&batchv2.CronJob{}).
+ WithValidator(&CronJobCustomValidator{}).
+ WithDefaulter(&CronJobCustomDefaulter{
+ DefaultConcurrencyPolicy: batchv2.AllowConcurrent,
+ DefaultSuspend: false,
+ DefaultSuccessfulJobsHistoryLimit: 3,
+ DefaultFailedJobsHistoryLimit: 1,
+ }).
+ Complete()
+}
+
+// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
+
+// +kubebuilder:webhook:path=/mutate-batch-tutorial-kubebuilder-io-v2-cronjob,mutating=true,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v2,name=mcronjob-v2.kb.io,admissionReviewVersions=v1
+
+// CronJobCustomDefaulter struct is responsible for setting default values on the custom resource of the
+// Kind CronJob when those are created or updated.
+//
+// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
+// as it is used only for temporary operations and does not need to be deeply copied.
+type CronJobCustomDefaulter struct {
+ // Default values for various CronJob fields
+ DefaultConcurrencyPolicy batchv2.ConcurrencyPolicy
+ DefaultSuspend bool
+ DefaultSuccessfulJobsHistoryLimit int32
+ DefaultFailedJobsHistoryLimit int32
+}
+
+var _ webhook.CustomDefaulter = &CronJobCustomDefaulter{}
+
+// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind CronJob.
+func (d *CronJobCustomDefaulter) Default(_ context.Context, obj runtime.Object) error {
+ cronjob, ok := obj.(*batchv2.CronJob)
+
+ if !ok {
+ return fmt.Errorf("expected an CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Defaulting for CronJob", "name", cronjob.GetName())
+
+ // Set default values
+ d.applyDefaults(cronjob)
+ return nil
+
+}
+
+// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
+// NOTE: If you want to customise the 'path', use the flags '--defaulting-path' or '--validation-path'.
+// +kubebuilder:webhook:path=/validate-batch-tutorial-kubebuilder-io-v2-cronjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,verbs=create;update,versions=v2,name=vcronjob-v2.kb.io,admissionReviewVersions=v1
+
+// CronJobCustomValidator struct is responsible for validating the CronJob resource
+// when it is created, updated, or deleted.
+//
+// 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
+}
+
+var _ webhook.CustomValidator = &CronJobCustomValidator{}
+
+// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := obj.(*batchv2.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Validation for CronJob upon creation", "name", cronjob.GetName())
+
+ return nil, validateCronJob(cronjob)
+}
+
+// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := newObj.(*batchv2.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object for the newObj but got %T", newObj)
+ }
+ cronjoblog.Info("Validation for CronJob upon update", "name", cronjob.GetName())
+
+ return nil, validateCronJob(cronjob)
+}
+
+// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type CronJob.
+func (v *CronJobCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
+ cronjob, ok := obj.(*batchv2.CronJob)
+ if !ok {
+ return nil, fmt.Errorf("expected a CronJob object but got %T", obj)
+ }
+ cronjoblog.Info("Validation for CronJob upon deletion", "name", cronjob.GetName())
+
+ // TODO(user): fill in your validation logic upon object deletion.
+
+ return nil, nil
+}
+
+// applyDefaults applies default values to CronJob fields.
+func (d *CronJobCustomDefaulter) applyDefaults(cronJob *batchv2.CronJob) {
+ if cronJob.Spec.ConcurrencyPolicy == "" {
+ cronJob.Spec.ConcurrencyPolicy = d.DefaultConcurrencyPolicy
+ }
+ if cronJob.Spec.Suspend == nil {
+ cronJob.Spec.Suspend = new(bool)
+ *cronJob.Spec.Suspend = d.DefaultSuspend
+ }
+ if cronJob.Spec.SuccessfulJobsHistoryLimit == nil {
+ cronJob.Spec.SuccessfulJobsHistoryLimit = new(int32)
+ *cronJob.Spec.SuccessfulJobsHistoryLimit = d.DefaultSuccessfulJobsHistoryLimit
+ }
+ if cronJob.Spec.FailedJobsHistoryLimit == nil {
+ cronJob.Spec.FailedJobsHistoryLimit = new(int32)
+ *cronJob.Spec.FailedJobsHistoryLimit = d.DefaultFailedJobsHistoryLimit
+ }
+}
+
+// validateCronJob validates the fields of a CronJob object.
+func validateCronJob(cronjob *batchv2.CronJob) error {
+ var allErrs field.ErrorList
+ if err := validateCronJobName(cronjob); err != nil {
+ allErrs = append(allErrs, err)
+ }
+ if err := validateCronJobSpec(cronjob); err != nil {
+ allErrs = append(allErrs, err)
+ }
+ if len(allErrs) == 0 {
+ return nil
+ }
+ return apierrors.NewInvalid(schema.GroupKind{Group: "batch.tutorial.kubebuilder.io", Kind: "CronJob"}, cronjob.Name, allErrs)
+}
+
+func validateCronJobName(cronjob *batchv2.CronJob) *field.Error {
+ if len(cronjob.Name) > validationutils.DNS1035LabelMaxLength-11 {
+ return field.Invalid(field.NewPath("metadata").Child("name"), cronjob.Name, "must be no more than 52 characters")
+ }
+ return nil
+}
+
+// validateCronJobSpec validates the schedule format of the custom CronSchedule type
+func validateCronJobSpec(cronjob *batchv2.CronJob) *field.Error {
+ // Build cron expression from the parts
+ parts := []string{"*", "*", "*", "*", "*"} // default parts for minute, hour, day of month, month, day of week
+ if cronjob.Spec.Schedule.Minute != nil {
+ parts[0] = string(*cronjob.Spec.Schedule.Minute) // Directly cast CronField (which is an alias of string) to string
+ }
+ if cronjob.Spec.Schedule.Hour != nil {
+ parts[1] = string(*cronjob.Spec.Schedule.Hour)
+ }
+ if cronjob.Spec.Schedule.DayOfMonth != nil {
+ parts[2] = string(*cronjob.Spec.Schedule.DayOfMonth)
+ }
+ if cronjob.Spec.Schedule.Month != nil {
+ parts[3] = string(*cronjob.Spec.Schedule.Month)
+ }
+ if cronjob.Spec.Schedule.DayOfWeek != nil {
+ parts[4] = string(*cronjob.Spec.Schedule.DayOfWeek)
+ }
+
+ // Join parts to form the full cron expression
+ cronExpression := strings.Join(parts, " ")
+
+ return validateScheduleFormat(
+ cronExpression,
+ field.NewPath("spec").Child("schedule"))
+}
+
+func validateScheduleFormat(schedule string, fldPath *field.Path) *field.Error {
+ if _, err := cron.ParseStandard(schedule); err != nil {
+ return field.Invalid(fldPath, schedule, "invalid cron schedule format: "+err.Error())
+ }
+ return nil
+}
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
new file mode 100644
index 00000000000..1ee7db018f7
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/cronjob_webhook_test.go
@@ -0,0 +1,87 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v2
+
+import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ batchv2 "tutorial.kubebuilder.io/project/api/v2"
+ // TODO (user): Add any additional imports if needed
+)
+
+var _ = Describe("CronJob Webhook", func() {
+ var (
+ obj *batchv2.CronJob
+ oldObj *batchv2.CronJob
+ validator CronJobCustomValidator
+ defaulter CronJobCustomDefaulter
+ )
+
+ BeforeEach(func() {
+ obj = &batchv2.CronJob{}
+ oldObj = &batchv2.CronJob{}
+ validator = CronJobCustomValidator{}
+ Expect(validator).NotTo(BeNil(), "Expected validator to be initialized")
+ defaulter = CronJobCustomDefaulter{}
+ Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized")
+ Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
+ Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
+ // TODO (user): Add any setup logic common to all tests
+ })
+
+ AfterEach(func() {
+ // TODO (user): Add any teardown logic common to all tests
+ })
+
+ Context("When creating CronJob under Defaulting Webhook", func() {
+ // TODO (user): Add logic for defaulting webhooks
+ // Example:
+ // It("Should apply defaults when a required field is empty", func() {
+ // By("simulating a scenario where defaults should be applied")
+ // obj.SomeFieldWithDefault = ""
+ // By("calling the Default method to apply defaults")
+ // defaulter.Default(ctx, obj)
+ // By("checking that the default values are set")
+ // Expect(obj.SomeFieldWithDefault).To(Equal("default_value"))
+ // })
+ })
+
+ Context("When creating or updating CronJob under Validating Webhook", func() {
+ // TODO (user): Add logic for validating webhooks
+ // Example:
+ // It("Should deny creation if a required field is missing", func() {
+ // By("simulating an invalid creation scenario")
+ // obj.SomeRequiredField = ""
+ // Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred())
+ // })
+ //
+ // It("Should admit creation if all required fields are present", func() {
+ // By("simulating an invalid creation scenario")
+ // obj.SomeRequiredField = "valid_value"
+ // Expect(validator.ValidateCreate(ctx, obj)).To(BeNil())
+ // })
+ //
+ // It("Should validate updates correctly", func() {
+ // By("simulating a valid update scenario")
+ // oldObj.SomeRequiredField = "updated_value"
+ // obj.SomeRequiredField = "updated_value"
+ // Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(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
new file mode 100644
index 00000000000..6477f2f6850
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/internal/webhook/v2/webhook_suite_test.go
@@ -0,0 +1,164 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v2
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "net"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "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 (
+ ctx context.Context
+ cancel context.CancelFunc
+ k8sClient client.Client
+ cfg *rest.Config
+ testEnv *envtest.Environment
+)
+
+func TestAPIs(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Webhook Suite")
+}
+
+var _ = BeforeSuite(func() {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
+
+ ctx, cancel = context.WithCancel(context.TODO())
+
+ var err error
+ err = batchv2.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: false,
+
+ WebhookInstallOptions: envtest.WebhookInstallOptions{
+ Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")},
+ },
+ }
+
+ // 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())
+
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ // start webhook server using Manager.
+ webhookInstallOptions := &testEnv.WebhookInstallOptions
+ mgr, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ WebhookServer: webhook.NewServer(webhook.Options{
+ Host: webhookInstallOptions.LocalServingHost,
+ Port: webhookInstallOptions.LocalServingPort,
+ CertDir: webhookInstallOptions.LocalServingCertDir,
+ }),
+ LeaderElection: false,
+ Metrics: metricsserver.Options{BindAddress: "0"},
+ })
+ Expect(err).NotTo(HaveOccurred())
+
+ err = SetupCronJobWebhookWithManager(mgr)
+ Expect(err).NotTo(HaveOccurred())
+
+ // +kubebuilder:scaffold:webhook
+
+ go func() {
+ defer GinkgoRecover()
+ err = mgr.Start(ctx)
+ Expect(err).NotTo(HaveOccurred())
+ }()
+
+ // wait for the webhook server to get ready.
+ dialer := &net.Dialer{Timeout: time.Second}
+ addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
+ Eventually(func() error {
+ conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
+ if err != nil {
+ return err
+ }
+
+ return conn.Close()
+ }).Should(Succeed())
+})
+
+var _ = AfterSuite(func() {
+ By("tearing down the test environment")
+ cancel()
+ 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/main.go b/docs/book/src/multiversion-tutorial/testdata/project/main.go
deleted file mode 100644
index cf42a4db4a4..00000000000
--- a/docs/book/src/multiversion-tutorial/testdata/project/main.go
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
-Copyright 2021 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
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-// +kubebuilder:docs-gen:collapse=Apache License
-
-package main
-
-import (
- "flag"
- "os"
-
- kbatchv1 "k8s.io/api/batch/v1"
- "k8s.io/apimachinery/pkg/runtime"
- 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/healthz"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
-
- batchv1 "tutorial.kubebuilder.io/project/api/v1"
- batchv2 "tutorial.kubebuilder.io/project/api/v2"
- "tutorial.kubebuilder.io/project/controllers"
- //+kubebuilder:scaffold:imports
-)
-
-// +kubebuilder:docs-gen:collapse=Imports
-
-/*
- */
-var (
- scheme = runtime.NewScheme()
- setupLog = ctrl.Log.WithName("setup")
-)
-
-func init() {
- utilruntime.Must(clientgoscheme.AddToScheme(scheme))
-
- utilruntime.Must(kbatchv1.AddToScheme(scheme)) // we've added this ourselves
- utilruntime.Must(batchv1.AddToScheme(scheme))
- utilruntime.Must(batchv2.AddToScheme(scheme))
- //+kubebuilder:scaffold:scheme
-}
-
-// +kubebuilder:docs-gen:collapse=existing setup
-
-/*
- */
-func main() {
- /*
- */
- var metricsAddr string
- var enableLeaderElection bool
- var probeAddr string
- flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
- flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
- flag.BoolVar(&enableLeaderElection, "leader-elect", false,
- "Enable leader election for controller manager. "+
- "Enabling this will ensure there is only one active controller manager.")
-
- opts := zap.Options{
- Development: true,
- }
- opts.BindFlags(flag.CommandLine)
- flag.Parse()
-
- ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
-
- mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
- Scheme: scheme,
- MetricsBindAddress: metricsAddr,
- Port: 9443,
- HealthProbeBindAddress: probeAddr,
- LeaderElection: enableLeaderElection,
- LeaderElectionID: "80807133.tutorial.kubebuilder.io",
- })
- if err != nil {
- setupLog.Error(err, "unable to start manager")
- os.Exit(1)
- }
-
- if err = (&controllers.CronJobReconciler{
- Client: mgr.GetClient(),
- Log: ctrl.Log.WithName("controllers").WithName("CronJob"),
- Scheme: mgr.GetScheme(),
- }).SetupWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create controller", "controller", "CronJob")
- os.Exit(1)
- }
-
- // +kubebuilder:docs-gen:collapse=existing setup
-
- /*
- Our existing call to SetupWebhookWithManager registers our conversion webhooks with the manager, too.
- */
- if os.Getenv("ENABLE_WEBHOOKS") != "false" {
- if err = (&batchv1.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
- os.Exit(1)
- }
- if err = (&batchv2.CronJob{}).SetupWebhookWithManager(mgr); err != nil {
- setupLog.Error(err, "unable to create webhook", "webhook", "CronJob")
- os.Exit(1)
- }
- }
- //+kubebuilder:scaffold:builder
-
- /*
- */
-
- if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
- setupLog.Error(err, "unable to set up health check")
- os.Exit(1)
- }
- if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
- setupLog.Error(err, "unable to set up ready check")
- os.Exit(1)
- }
-
- setupLog.Info("starting manager")
- if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
- setupLog.Error(err, "problem running manager")
- os.Exit(1)
- }
- // +kubebuilder:docs-gen:collapse=existing setup
-}
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
new file mode 100644
index 00000000000..c33c3bb67ef
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_suite_test.go
@@ -0,0 +1,115 @@
+//go:build e2e
+// +build e2e
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "tutorial.kubebuilder.io/project/test/utils"
+)
+
+var (
+ // Optional Environment Variables:
+ // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup.
+ // These variables are useful if CertManager is already installed, avoiding
+ // re-installation and conflicts.
+ skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true"
+ // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster
+ isCertManagerAlreadyInstalled = false
+ // isPrometheusOperatorAlreadyInstalled will be set true when prometheus CRDs be found on the cluster
+ isPrometheusOperatorAlreadyInstalled = false
+
+ // projectImage is the name of the image which will be build and loaded
+ // with the code source changes to be tested.
+ projectImage = "example.com/project:v0.0.1"
+)
+
+// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated,
+// temporary environment to validate project changes with the purpose of being used in CI jobs.
+// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs
+// CertManager.
+func TestE2E(t *testing.T) {
+ RegisterFailHandler(Fail)
+ _, _ = fmt.Fprintf(GinkgoWriter, "Starting project integration test suite\n")
+ RunSpecs(t, "e2e suite")
+}
+
+var _ = BeforeSuite(func() {
+ By("Ensure that Prometheus is enabled")
+ _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#")
+
+ By("building the manager(Operator) image")
+ cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage))
+ _, err := utils.Run(cmd)
+ ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image")
+
+ // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is
+ // built and available before running the tests. Also, remove the following block.
+ By("loading the manager(Operator) image on Kind")
+ err = utils.LoadImageToKindClusterWithName(projectImage)
+ ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind")
+
+ // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing.
+ // To prevent errors when tests run in environments with Prometheus already installed,
+ // we check for its presence before execution.
+ // Setup Prometheus before the suite if not already installed
+ By("checking if prometheus is installed already")
+ isPrometheusOperatorAlreadyInstalled = utils.IsPrometheusCRDsInstalled()
+ if !isPrometheusOperatorAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n")
+ Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator")
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: Prometheus Operator is already installed. Skipping installation...\n")
+ }
+
+ // To prevent errors when tests run in environments with CertManager already installed,
+ // we check for its presence before execution.
+ // Setup CertManager before the suite if not skipped and if not already installed
+ if !skipCertManagerInstall {
+ By("checking if cert manager is installed already")
+ isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled()
+ if !isCertManagerAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n")
+ Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager")
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n")
+ }
+ }
+})
+
+var _ = AfterSuite(func() {
+ // Teardown Prometheus after the suite if it was not already installed
+ if !isPrometheusOperatorAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n")
+ utils.UninstallPrometheusOperator()
+ }
+
+ // Teardown CertManager after the suite if not skipped and if it was not already installed
+ if !skipCertManagerInstall && !isCertManagerAlreadyInstalled {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n")
+ utils.UninstallCertManager()
+ }
+})
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
new file mode 100644
index 00000000000..d7f7aa49c12
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/test/e2e/e2e_test.go
@@ -0,0 +1,485 @@
+//go:build e2e
+// +build e2e
+
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package e2e
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "tutorial.kubebuilder.io/project/test/utils"
+)
+
+// namespace where the project is deployed in
+const namespace = "project-system"
+
+// serviceAccountName created for the project
+const serviceAccountName = "project-controller-manager"
+
+// metricsServiceName is the name of the metrics service of the project
+const metricsServiceName = "project-controller-manager-metrics-service"
+
+// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data
+const metricsRoleBindingName = "project-metrics-binding"
+
+var _ = Describe("Manager", Ordered, func() {
+ var controllerPodName string
+
+ // Before running the tests, set up the environment by creating the namespace,
+ // 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)
+ Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs")
+
+ By("deploying the controller-manager")
+ cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage))
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager")
+ })
+
+ // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs,
+ // and deleting the namespace.
+ AfterAll(func() {
+ By("cleaning up the curl pod for metrics")
+ cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace)
+ _, _ = utils.Run(cmd)
+
+ By("undeploying the controller-manager")
+ cmd = exec.Command("make", "undeploy")
+ _, _ = utils.Run(cmd)
+
+ By("uninstalling CRDs")
+ cmd = exec.Command("make", "uninstall")
+ _, _ = utils.Run(cmd)
+
+ By("removing manager namespace")
+ cmd = exec.Command("kubectl", "delete", "ns", namespace)
+ _, _ = utils.Run(cmd)
+ })
+
+ // After each test, check for failures and collect logs, events,
+ // and pod descriptions for debugging.
+ AfterEach(func() {
+ By("Cleaning up test CronJob resources")
+ cmd := exec.Command("kubectl", "delete", "-f", "config/samples/batch_v1_cronjob.yaml", "-n", namespace, "--ignore-not-found=true")
+ _, _ = utils.Run(cmd)
+ cmd = exec.Command("kubectl", "delete", "-f", "config/samples/batch_v2_cronjob.yaml", "-n", namespace, "--ignore-not-found=true")
+ _, _ = utils.Run(cmd)
+
+ specReport := CurrentSpecReport()
+ if specReport.Failed() {
+ By("Fetching controller manager pod logs")
+ cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
+ controllerLogs, err := utils.Run(cmd)
+ if err == nil {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs)
+ } else {
+ _, _ = 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, "Kubernetes events:\n%s", eventsOutput)
+ } else {
+ _, _ = 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, "Metrics logs:\n %s", metricsOutput)
+ } else {
+ _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err)
+ }
+
+ By("Fetching controller manager pod description")
+ cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace)
+ podDescription, err := utils.Run(cmd)
+ if err == nil {
+ fmt.Println("Pod description:\n", podDescription)
+ } else {
+ fmt.Println("Failed to describe controller pod")
+ }
+ }
+ })
+
+ SetDefaultEventuallyTimeout(2 * time.Minute)
+ SetDefaultEventuallyPollingInterval(time.Second)
+
+ Context("Manager", func() {
+ It("should run successfully", func() {
+ By("validating that the controller-manager pod is running as expected")
+ verifyControllerUp := func(g Gomega) {
+ // Get the name of the controller-manager pod
+ cmd := exec.Command("kubectl", "get",
+ "pods", "-l", "control-plane=controller-manager",
+ "-o", "go-template={{ range .items }}"+
+ "{{ if not .metadata.deletionTimestamp }}"+
+ "{{ .metadata.name }}"+
+ "{{ \"\\n\" }}{{ end }}{{ end }}",
+ "-n", namespace,
+ )
+
+ podOutput, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information")
+ podNames := utils.GetNonEmptyLines(podOutput)
+ g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running")
+ controllerPodName = podNames[0]
+ g.Expect(controllerPodName).To(ContainSubstring("controller-manager"))
+
+ // Validate the pod's status
+ cmd = exec.Command("kubectl", "get",
+ "pods", controllerPodName, "-o", "jsonpath={.status.phase}",
+ "-n", namespace,
+ )
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status")
+ }
+ Eventually(verifyControllerUp).Should(Succeed())
+ })
+
+ It("should ensure the metrics endpoint is serving metrics", func() {
+ By("creating a ClusterRoleBinding for the service account to allow access to metrics")
+ cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName,
+ "--clusterrole=project-metrics-reader",
+ fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName),
+ )
+ _, err := utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding")
+
+ By("validating that the metrics service is available")
+ cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace)
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Metrics service should exist")
+
+ By("validating that the ServiceMonitor for Prometheus is applied in the namespace")
+ cmd = exec.Command("kubectl", "get", "ServiceMonitor", "-n", namespace)
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "ServiceMonitor should exist")
+
+ By("getting the service account token")
+ token, err := serviceAccountToken()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(token).NotTo(BeEmpty())
+
+ By("ensuring the controller pod is ready")
+ verifyControllerPodReady := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "pod", controllerPodName, "-n", namespace,
+ "-o", "jsonpath={.status.conditions[?(@.type=='Ready')].status}")
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("True"), "Controller pod not ready")
+ }
+ Eventually(verifyControllerPodReady, 3*time.Minute, time.Second).Should(Succeed())
+
+ By("verifying that the controller manager is serving the metrics server")
+ verifyMetricsServerStarted := func(g Gomega) {
+ cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace)
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(ContainSubstring("Serving metrics server"),
+ "Metrics server not yet started")
+ }
+ Eventually(verifyMetricsServerStarted, 3*time.Minute, time.Second).Should(Succeed())
+
+ By("waiting for the webhook service endpoints to be ready")
+ verifyWebhookEndpointsReady := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "endpointslices.discovery.k8s.io", "-n", namespace,
+ "-l", "kubernetes.io/service-name=project-webhook-service",
+ "-o", "jsonpath={range .items[*]}{range .endpoints[*]}{.addresses[*]}{end}{end}")
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Webhook endpoints should exist")
+ g.Expect(output).ShouldNot(BeEmpty(), "Webhook endpoints not yet ready")
+ }
+ Eventually(verifyWebhookEndpointsReady, 3*time.Minute, time.Second).Should(Succeed())
+
+ // +kubebuilder:scaffold:e2e-metrics-webhooks-readiness
+
+ 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: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": {
+ "readOnlyRootFilesystem": true,
+ "allowPrivilegeEscalation": false,
+ "capabilities": {
+ "drop": ["ALL"]
+ },
+ "runAsNonRoot": true,
+ "runAsUser": 1000,
+ "seccompProfile": {
+ "type": "RuntimeDefault"
+ }
+ }
+ }],
+ "serviceAccountName": "%s"
+ }
+ }`, token, metricsServiceName, namespace, serviceAccountName))
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod")
+
+ By("waiting for the curl-metrics pod to complete.")
+ verifyCurlUp := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "pods", "curl-metrics",
+ "-o", "jsonpath={.status.phase}",
+ "-n", namespace)
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status")
+ }
+ Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed())
+
+ By("getting the metrics by checking curl-metrics logs")
+ verifyMetricsAvailable := func(g Gomega) {
+ metricsOutput, err := getMetricsOutput()
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
+ g.Expect(metricsOutput).NotTo(BeEmpty())
+ g.Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK"))
+ }
+ Eventually(verifyMetricsAvailable, 2*time.Minute).Should(Succeed())
+ })
+
+ It("should provisioned cert-manager", func() {
+ By("validating that cert-manager has the certificate Secret")
+ verifyCertManager := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "secrets", "webhook-server-cert", "-n", namespace)
+ _, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ }
+ Eventually(verifyCertManager).Should(Succeed())
+ })
+
+ It("should have CA injection for mutating webhooks", func() {
+ By("checking CA injection for mutating webhooks")
+ verifyCAInjection := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get",
+ "mutatingwebhookconfigurations.admissionregistration.k8s.io",
+ "project-mutating-webhook-configuration",
+ "-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
+ mwhOutput, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(len(mwhOutput)).To(BeNumerically(">", 10))
+ }
+ Eventually(verifyCAInjection).Should(Succeed())
+ })
+
+ It("should have CA injection for validating webhooks", func() {
+ By("checking CA injection for validating webhooks")
+ verifyCAInjection := func(g Gomega) {
+ cmd := exec.Command("kubectl", "get",
+ "validatingwebhookconfigurations.admissionregistration.k8s.io",
+ "project-validating-webhook-configuration",
+ "-o", "go-template={{ range .webhooks }}{{ .clientConfig.caBundle }}{{ end }}")
+ vwhOutput, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred())
+ g.Expect(len(vwhOutput)).To(BeNumerically(">", 10))
+ }
+ 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.
+ // Consider applying sample/CR(s) and check their status and/or verifying
+ // the reconciliation by using the metrics, i.e.:
+ // metricsOutput, err := getMetricsOutput()
+ // Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod")
+ // Expect(metricsOutput).To(ContainSubstring(
+ // fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`,
+ // strings.ToLower(),
+ // ))
+ It("should successfully convert between v1 and v2 versions", func() {
+ By("waiting for the webhook service to be ready")
+ Eventually(func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "endpoints", "-n", namespace,
+ "-l", "control-plane=controller-manager",
+ "-o", "jsonpath={.items[0].subsets[0].addresses[0].ip}")
+ output, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to get webhook service endpoints")
+ g.Expect(strings.TrimSpace(output)).NotTo(BeEmpty(), "Webhook endpoint should have an IP")
+ }, time.Minute, time.Second).Should(Succeed())
+
+ By("creating a v1 CronJob with a specific schedule")
+ cmd := exec.Command("kubectl", "apply", "-f", "config/samples/batch_v1_cronjob.yaml", "-n", namespace)
+ _, err := utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create v1 CronJob")
+
+ By("waiting for the v1 CronJob to be created")
+ Eventually(func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "cronjob.batch.tutorial.kubebuilder.io", "cronjob-sample", "-n", namespace)
+ output, err := utils.Run(cmd)
+ if err != nil {
+ // Log controller logs on failure for debugging
+ logCmd := exec.Command("kubectl", "logs", "-l", "control-plane=controller-manager", "-n", namespace, "--tail=50")
+ logs, _ := utils.Run(logCmd)
+ _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs when CronJob not found:\n%s\n", logs)
+ }
+ g.Expect(err).NotTo(HaveOccurred(), "v1 CronJob should exist, output: "+output)
+ }, time.Minute, time.Second).Should(Succeed())
+
+ By("fetching the v1 CronJob and verifying the schedule format")
+ cmd = exec.Command("kubectl", "get", "cronjob.v1.batch.tutorial.kubebuilder.io", "cronjob-sample",
+ "-n", namespace, "-o", "jsonpath={.spec.schedule}")
+ v1Schedule, err := utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to get v1 CronJob schedule")
+ Expect(strings.TrimSpace(v1Schedule)).To(Equal("*/1 * * * *"),
+ "v1 schedule should be in cron format")
+
+ By("fetching the same CronJob as v2 and verifying the converted schedule")
+ Eventually(func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "cronjob.v2.batch.tutorial.kubebuilder.io", "cronjob-sample",
+ "-n", namespace, "-o", "jsonpath={.spec.schedule.minute}")
+ v2Minute, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to get v2 CronJob schedule")
+ g.Expect(strings.TrimSpace(v2Minute)).To(Equal("*/1"),
+ "v2 schedule.minute should be converted from v1 schedule")
+ }, time.Minute, time.Second).Should(Succeed())
+
+ By("creating a v2 CronJob with structured schedule fields")
+ cmd = exec.Command("kubectl", "apply", "-f", "config/samples/batch_v2_cronjob.yaml", "-n", namespace)
+ _, err = utils.Run(cmd)
+ Expect(err).NotTo(HaveOccurred(), "Failed to create v2 CronJob")
+
+ By("verifying the v2 CronJob has the correct structured schedule")
+ Eventually(func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "cronjob.v2.batch.tutorial.kubebuilder.io", "cronjob-sample",
+ "-n", namespace, "-o", "jsonpath={.spec.schedule.minute}")
+ v2Minute, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to get v2 CronJob schedule")
+ g.Expect(strings.TrimSpace(v2Minute)).To(Equal("*/1"),
+ "v2 CronJob should have minute field set")
+ }, time.Minute, time.Second).Should(Succeed())
+
+ By("fetching the v2 CronJob as v1 and verifying schedule conversion")
+ Eventually(func(g Gomega) {
+ cmd := exec.Command("kubectl", "get", "cronjob.v1.batch.tutorial.kubebuilder.io", "cronjob-sample",
+ "-n", namespace, "-o", "jsonpath={.spec.schedule}")
+ v1Schedule, err := utils.Run(cmd)
+ g.Expect(err).NotTo(HaveOccurred(), "Failed to get converted v1 schedule")
+ // When v2 only has minute field set, it converts to "*/1 * * * *"
+ g.Expect(strings.TrimSpace(v1Schedule)).To(Equal("*/1 * * * *"),
+ "v1 schedule should be converted from v2 structured schedule")
+ }, time.Minute, time.Second).Should(Succeed())
+ })
+ })
+})
+
+// serviceAccountToken returns a token for the specified service account in the given namespace.
+// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request
+// and parsing the resulting token from the API response.
+func serviceAccountToken() (string, error) {
+ const tokenRequestRawString = `{
+ "apiVersion": "authentication.k8s.io/v1",
+ "kind": "TokenRequest"
+ }`
+
+ // Temporary file to store the token request
+ secretName := fmt.Sprintf("%s-token-request", serviceAccountName)
+ tokenRequestFile := filepath.Join("/tmp", secretName)
+ err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644))
+ if err != nil {
+ return "", err
+ }
+
+ var out string
+ verifyTokenCreation := func(g Gomega) {
+ // Execute kubectl command to create the token
+ cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf(
+ "/api/v1/namespaces/%s/serviceaccounts/%s/token",
+ namespace,
+ serviceAccountName,
+ ), "-f", tokenRequestFile)
+
+ output, err := cmd.CombinedOutput()
+ g.Expect(err).NotTo(HaveOccurred())
+
+ // Parse the JSON output to extract the token
+ var token tokenRequest
+ err = json.Unmarshal(output, &token)
+ g.Expect(err).NotTo(HaveOccurred())
+
+ out = token.Status.Token
+ }
+ Eventually(verifyTokenCreation).Should(Succeed())
+
+ return out, err
+}
+
+// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint.
+func getMetricsOutput() (string, error) {
+ By("getting the curl-metrics logs")
+ cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace)
+ return utils.Run(cmd)
+}
+
+// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response,
+// containing only the token field that we need to extract.
+type tokenRequest struct {
+ Status struct {
+ Token string `json:"token"`
+ } `json:"status"`
+}
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
new file mode 100644
index 00000000000..2653bcd6c8c
--- /dev/null
+++ b/docs/book/src/multiversion-tutorial/testdata/project/test/utils/utils.go
@@ -0,0 +1,274 @@
+/*
+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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package utils
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+
+ . "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck
+)
+
+const (
+ certmanagerVersion = "v1.19.1"
+ certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
+
+ defaultKindBinary = "kind"
+ defaultKindCluster = "kind"
+
+ prometheusOperatorVersion = "v0.85.0"
+ prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" +
+ "releases/download/%s/bundle.yaml"
+)
+
+func warnError(err error) {
+ _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
+}
+
+// Run executes the provided command within this context
+func Run(cmd *exec.Cmd) (string, error) {
+ dir, _ := GetProjectDir()
+ cmd.Dir = dir
+
+ if err := os.Chdir(cmd.Dir); err != nil {
+ _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err)
+ }
+
+ cmd.Env = append(os.Environ(), "GO111MODULE=on")
+ command := strings.Join(cmd.Args, " ")
+ _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err)
+ }
+
+ return string(output), nil
+}
+
+// UninstallCertManager uninstalls the cert manager
+func UninstallCertManager() {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+
+ // Delete leftover leases in kube-system (not cleaned by default)
+ kubeSystemLeases := []string{
+ "cert-manager-cainjector-leader-election",
+ "cert-manager-controller",
+ }
+ for _, lease := range kubeSystemLeases {
+ cmd = exec.Command("kubectl", "delete", "lease", lease,
+ "-n", "kube-system", "--ignore-not-found", "--force", "--grace-period=0")
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+ }
+}
+
+// InstallCertManager installs the cert manager bundle.
+func InstallCertManager() error {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "apply", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ return err
+ }
+ // Wait for cert-manager-webhook to be ready, which can take time if cert-manager
+ // was re-installed after uninstalling on a cluster.
+ cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
+ "--for", "condition=Available",
+ "--namespace", "cert-manager",
+ "--timeout", "5m",
+ )
+
+ _, err := Run(cmd)
+ return err
+}
+
+// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed
+// by verifying the existence of key CRDs related to Cert Manager.
+func IsCertManagerCRDsInstalled() bool {
+ // List of common Cert Manager CRDs
+ certManagerCRDs := []string{
+ "certificates.cert-manager.io",
+ "issuers.cert-manager.io",
+ "clusterissuers.cert-manager.io",
+ "certificaterequests.cert-manager.io",
+ "orders.acme.cert-manager.io",
+ "challenges.acme.cert-manager.io",
+ }
+
+ // Execute the kubectl command to get all CRDs
+ cmd := exec.Command("kubectl", "get", "crds")
+ output, err := Run(cmd)
+ if err != nil {
+ return false
+ }
+
+ // Check if any of the Cert Manager CRDs are present
+ crdList := GetNonEmptyLines(output)
+ for _, crd := range certManagerCRDs {
+ for _, line := range crdList {
+ if strings.Contains(line, crd) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics.
+func InstallPrometheusOperator() error {
+ url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
+ cmd := exec.Command("kubectl", "create", "-f", url)
+ _, err := Run(cmd)
+ return err
+}
+
+// UninstallPrometheusOperator uninstalls the prometheus
+func UninstallPrometheusOperator() {
+ url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+}
+
+// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed
+// by verifying the existence of key CRDs related to Prometheus.
+func IsPrometheusCRDsInstalled() bool {
+ // List of common Prometheus CRDs
+ prometheusCRDs := []string{
+ "prometheuses.monitoring.coreos.com",
+ "prometheusrules.monitoring.coreos.com",
+ "prometheusagents.monitoring.coreos.com",
+ }
+
+ cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name")
+ output, err := Run(cmd)
+ if err != nil {
+ return false
+ }
+ crdList := GetNonEmptyLines(output)
+ for _, crd := range prometheusCRDs {
+ for _, line := range crdList {
+ if strings.Contains(line, crd) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+// LoadImageToKindClusterWithName loads a local docker image to the kind cluster
+func LoadImageToKindClusterWithName(name string) error {
+ cluster := defaultKindCluster
+ if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
+ cluster = v
+ }
+ kindOptions := []string{"load", "docker-image", name, "--name", cluster}
+ kindBinary := defaultKindBinary
+ if v, ok := os.LookupEnv("KIND"); ok {
+ kindBinary = v
+ }
+ cmd := exec.Command(kindBinary, kindOptions...)
+ _, err := Run(cmd)
+ return err
+}
+
+// GetNonEmptyLines converts given command output string into individual objects
+// according to line breakers, and ignores the empty elements in it.
+func GetNonEmptyLines(output string) []string {
+ var res []string
+ elements := strings.Split(output, "\n")
+ for _, element := range elements {
+ if element != "" {
+ res = append(res, element)
+ }
+ }
+
+ return res
+}
+
+// GetProjectDir will return the directory where the project is
+func GetProjectDir() (string, error) {
+ wd, err := os.Getwd()
+ if err != nil {
+ return wd, fmt.Errorf("failed to get current working directory: %w", err)
+ }
+ wd = strings.ReplaceAll(wd, "/test/e2e", "")
+ return wd, nil
+}
+
+// UncommentCode searches for target in the file and remove the comment prefix
+// of the target content. The target content may span multiple lines.
+func UncommentCode(filename, target, prefix string) error {
+ // false positive
+ // nolint:gosec
+ content, err := os.ReadFile(filename)
+ if err != nil {
+ return fmt.Errorf("failed to read file %q: %w", filename, err)
+ }
+ strContent := string(content)
+
+ idx := strings.Index(strContent, target)
+ if idx < 0 {
+ return fmt.Errorf("unable to find the code %q to be uncomment", target)
+ }
+
+ out := new(bytes.Buffer)
+ _, err = out.Write(content[:idx])
+ if err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+
+ scanner := bufio.NewScanner(bytes.NewBufferString(target))
+ if !scanner.Scan() {
+ return nil
+ }
+ for {
+ if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+ // Avoid writing a newline in case the previous line was the last in target.
+ if !scanner.Scan() {
+ break
+ }
+ if _, err = out.WriteString("\n"); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+ }
+
+ if _, err = out.Write(content[idx+len(target):]); err != nil {
+ return fmt.Errorf("failed to write to output: %w", err)
+ }
+
+ // false positive
+ // nolint:gosec
+ if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil {
+ return fmt.Errorf("failed to write file %q: %w", filename, err)
+ }
+
+ return nil
+}
diff --git a/docs/book/src/multiversion-tutorial/tutorial.md b/docs/book/src/multiversion-tutorial/tutorial.md
index 54bc4457cb5..192b7498a08 100644
--- a/docs/book/src/multiversion-tutorial/tutorial.md
+++ b/docs/book/src/multiversion-tutorial/tutorial.md
@@ -23,22 +23,4 @@ form a runnable project, and live in the book source directory:
-
-
-Minimum Kubernetes Versions Incoming!
-
-CRD conversion support was introduced as an alpha feature in Kubernetes
-1.13 (which means it's not on by default, and needs to be enabled via
-a [feature gate][kube-feature-gates]), and became beta in Kubernetes 1.15
-(which means it's on by default).
-
-If you're on Kubernetes 1.13-1.14, make sure to enable the feature gate.
-If you're on Kubernetes 1.12 or below, you'll need a new cluster to use
-conversion. Check out the [KinD instructions](/reference/kind.md) for
-instructions on how to set up a all-in-one cluster.
-
-
-
Next, let's figure out what changes we want to make...
-
-[kube-feature-gates]: https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/ "Kubernetes Feature Gates"
diff --git a/docs/book/src/multiversion-tutorial/webhooks.md b/docs/book/src/multiversion-tutorial/webhooks.md
index be87f45d721..77c64771e06 100644
--- a/docs/book/src/multiversion-tutorial/webhooks.md
+++ b/docs/book/src/multiversion-tutorial/webhooks.md
@@ -3,24 +3,26 @@
Our conversion is in place, so all that's left is to tell
controller-runtime about our conversion.
-Normally, we'd run
+## Webhook setup for v1...
-```shell
-kubebuilder create webhook --group batch --version v1 --kind CronJob --conversion
-```
+The v1 webhook handles conversion (as the hub) and provides validation/defaulting
+for the v1 CronJob format with a string-based schedule:
-to scaffold out the webhook setup. However, we've already got webhook
-setup, from when we built our defaulting and validating webhooks!
+{{#literatego ./testdata/project/internal/webhook/v1/cronjob_webhook.go}}
-## Webhook setup...
+## Webhook setup for v2...
-{{#literatego ./testdata/project/api/v1/cronjob_webhook.go}}
+The v2 webhook provides validation and defaulting for the v2 CronJob format
+with the structured CronSchedule type. Note how the validation logic differs
+from v1 - it builds a cron expression from the individual schedule fields:
+
+{{#literatego ./testdata/project/internal/webhook/v2/cronjob_webhook.go}}
## ...and `main.go`
Similarly, our existing main file is sufficient:
-{{#literatego ./testdata/project/main.go}}
+{{#literatego ./testdata/project/cmd/main.go}}
Everything's set up and ready to go! All that's left now is to test out
our webhooks.
diff --git a/docs/book/src/plugins/available-plugins.md b/docs/book/src/plugins/available-plugins.md
new file mode 100644
index 00000000000..b4d819b8a6f
--- /dev/null
+++ b/docs/book/src/plugins/available-plugins.md
@@ -0,0 +1,9 @@
+# Available plugins
+
+This section describes the plugins supported and shipped in with the Kubebuilder project.
+
+{{#include to-scaffold-project.md }}
+{{#include to-add-optional-features.md }}
+{{#include to-be-extended.md }}
+
+[plugin-versions]: plugins-versioning.md
\ No newline at end of file
diff --git a/docs/book/src/plugins/available/autoupdate-v1-alpha.md b/docs/book/src/plugins/available/autoupdate-v1-alpha.md
new file mode 100644
index 00000000000..a4312008c8f
--- /dev/null
+++ b/docs/book/src/plugins/available/autoupdate-v1-alpha.md
@@ -0,0 +1,86 @@
+# AutoUpdate (`autoupdate/v1-alpha`)
+
+Keeping your Kubebuilder project up to date with the latest improvements shouldn’t be a chore.
+With a small amount of setup, you can receive **automatic Pull Request** suggestions whenever a new
+Kubebuilder release is available — keeping your project **maintained, secure, and aligned with ecosystem changes**.
+
+This automation uses the [`kubebuilder alpha update`][alpha-update-command] command with a **3-way merge strategy** to
+refresh your project scaffold, and wraps it in a GitHub Actions workflow that opens an **Issue** with a **Pull Request compare link** so you can create the PR and review it.
+
+
+Protect your branches
+
+This workflow by default **only** creates and pushes the merged files to a branch
+called `kubebuilder-update-from--to-`.
+
+To keep your codebase safe, use branch protection rules to ensure that
+changes aren't pushed or merged without proper review.
+
+
+
+## When to Use It
+
+- When you don’t deviate too much from the default scaffold — ensure that you see the note about customization [here](https://book.kubebuilder.io/versions_compatibility_supportability#project-customizations).
+- When you want to reduce the burden of keeping the project updated and well-maintained.
+- When you want to guidance and help from AI to know what changes are needed to keep your project up to date
+as to solve conflicts.
+
+## How to Use It
+
+- If you want to add the `autoupdate` plugin to your project:
+
+```shell
+kubebuilder edit --plugins="autoupdate.kubebuilder.io/v1-alpha"
+```
+
+- If you want to create a new project with the `autoupdate` plugin:
+
+```shell
+kubebuilder init --plugins=go/v4,autoupdate/v1-alpha
+```
+
+## How It Works
+
+This will scaffold a GitHub Actions workflow that runs the [kubebuilder alpha update][alpha-update-command] command.
+Whenever a new Kubebuilder release is available, the workflow will automatically open an **Issue** with a Pull Request compare link so you can easily create the PR and review it, such as:
+
+
+
+By default, the workflow scaffolded uses `--use-gh-model` the flag to leverage in [AI models][ai-models] to help you understand
+what changes are needed. You'll get a concise list of changed files to streamline the review, for example:
+
+
+
+If conflicts arise, AI-generated comments call them out and provide next steps, such as:
+
+
+
+### Workflow details
+
+The workflow will check once a week for new releases, and if there are any, it will create an Issue with a Pull Request compare link so you can create the PR and review it.
+The command called by the workflow is:
+
+```shell
+ # More info: https://kubebuilder.io/reference/commands/alpha_update
+ - name: Run kubebuilder alpha update
+ run: |
+ # Executes the update command with specified flags.
+ # --force: Completes the merge even if conflicts occur, leaving conflict markers.
+ # --push: Automatically pushes the resulting output branch to the 'origin' remote.
+ # --restore-path: Preserves specified paths (e.g., CI workflow files) when squashing.
+ # --open-gh-models: Adds an AI-generated comment to the created Issue with
+ # a short overview of the scaffold changes and conflict-resolution guidance (If Any).
+ kubebuilder alpha update \
+ --force \
+ --push \
+ --restore-path .github/workflows \
+ --open-gh-issue \
+ --use-gh-models
+```
+
+## Demonstration
+
+VIDEO
+
+[alpha-update-command]: ./../../reference/commands/alpha_update.md
+[ai-models]: https://docs.github.com/en/github-models/about-github-models
diff --git a/docs/book/src/plugins/available/deploy-image-plugin-v1-alpha.md b/docs/book/src/plugins/available/deploy-image-plugin-v1-alpha.md
new file mode 100644
index 00000000000..cfa88033e98
--- /dev/null
+++ b/docs/book/src/plugins/available/deploy-image-plugin-v1-alpha.md
@@ -0,0 +1,111 @@
+# Deploy Image Plugin (deploy-image/v1-alpha)
+
+The `deploy-image` plugin allows users to create [controllers][controller-runtime] and custom resources that deploy and manage container images on the cluster, following Kubernetes best practices. It simplifies the complexities of deploying images while allowing users to customize their projects as needed.
+
+By using this plugin, you will get:
+
+- A controller implementation to deploy and manage an Operand (image) on the cluster.
+- Tests to verify the reconciliation logic, using [ENVTEST][envtest].
+- Custom resource samples updated with the necessary specifications.
+- Environment variable support for managing the Operand (image) within the manager.
+
+
+Examples
+
+See the `project-v4-with-plugins` directory under the [testdata][testdata]
+directory in the Kubebuilder project to check an example
+of scaffolding created using this plugin.
+
+The `Memcached` API and its controller was scaffolded
+using the command:
+
+```shell
+kubebuilder create api \
+ --group example.com \
+ --version v1alpha1 \
+ --kind Memcached \
+ --image=memcached:memcached:1.6.26-alpine3.19 \
+ --image-container-command="memcached,--memory-limit=64,-o,modern,-v" \
+ --image-container-port="11211" \
+ --run-as-user="1001" \
+ --plugins="deploy-image/v1-alpha"
+```
+
+The `Busybox` API was created with:
+
+```shell
+kubebuilder create api \
+ --group example.com \
+ --version v1alpha1 \
+ --kind Busybox \
+ --image=busybox:1.36.1 \
+ --plugins="deploy-image/v1-alpha"
+```
+
+
+
+## When to use it?
+
+- This plugin is ideal for users who are just getting started with Kubernetes operators.
+- It helps users deploy and manage an image (Operand) using the [Operator pattern][operator-pattern].
+- If you're looking for a quick and efficient way to set up a custom controller and manage a container image, this plugin is a great choice.
+
+## How to use it?
+
+1. **Initialize your project**:
+ After creating a new project with `kubebuilder init`, you can use this
+ plugin to create APIs. Ensure that you've completed the
+ [quick start][quick-start] guide before proceeding.
+
+2. **Create APIs**:
+ With this plugin, you can [create APIs][create-apis] to specify the image (Operand) you want to deploy on the cluster. You can also optionally specify the command, port, and security context using various flags:
+
+ Example command:
+ ```sh
+ kubebuilder create api --group example.com --version v1alpha1 --kind Memcached --image=memcached:1.6.15-alpine --image-container-command="memcached,--memory-limit=64,modern,-v" --image-container-port="11211" --run-as-user="1001" --plugins="deploy-image/v1-alpha"
+ ```
+
+
+Note on make run:
+
+When running the project locally with `make run`, the Operand image
+provided will be stored as an environment variable in the
+`config/manager/manager.yaml` file.
+
+Ensure you export the environment variable before running the project locally, such as:
+
+```shell
+export MEMCACHED_IMAGE="memcached:1.4.36-alpine"
+```
+
+
+
+## Subcommands
+
+The `deploy-image` plugin includes the following subcommand:
+
+- `create api`: Use this command to scaffold the API and controller code to manage the container image.
+
+## Affected files
+
+When using the `create api` command with this plugin, the following
+files are affected, in addition to the existing Kubebuilder scaffolding:
+
+- `controllers/*_controller_test.go`: Scaffolds tests for the controller.
+- `controllers/*_suite_test.go`: Scaffolds or updates the test suite.
+- `api//*_types.go`: Scaffolds the API specs.
+- `config/samples/*_.yaml`: Scaffolds default values for the custom resource.
+- `main.go`: Updates the file to add the controller setup.
+- `config/manager/manager.yaml`: Updates to include environment variables for storing the image.
+
+## Further Resources:
+
+- Check out this [video][video] to see how it works.
+
+[video]: https://youtu.be/UwPuRjjnMjY
+[operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[testdata]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata/project-v4-with-plugins
+[envtest]: ./../../reference/envtest.md
+[quick-start]: ./../../quick-start.md
+[create-apis]: ../../cronjob-tutorial/new-api.md
\ No newline at end of file
diff --git a/docs/book/src/plugins/available/go-v4-plugin.md b/docs/book/src/plugins/available/go-v4-plugin.md
new file mode 100644
index 00000000000..b16b21034bf
--- /dev/null
+++ b/docs/book/src/plugins/available/go-v4-plugin.md
@@ -0,0 +1,53 @@
+# go/v4 (go.kubebuilder.io/v4)
+
+**(Default Scaffold)**
+
+Kubebuilder will scaffold using the `go/v4` plugin only if specified when initializing the project.
+This plugin is a composition of the `kustomize.common.kubebuilder.io/v2` and `base.go.kubebuilder.io/v4` plugins
+using the [Bundle Plugin][bundle]. It scaffolds a project template
+that helps in constructing sets of [controllers][controller-runtime].
+
+By following the [quickstart][quickstart] and creating any project,
+you will be using this plugin by default.
+
+
+Examples
+
+You can check samples using this plugin by looking at the `project-v4-` projects under the [testdata][testdata]
+directory on the root directory of the Kubebuilder project.
+
+
+
+## How to use it ?
+
+To create a new project with the `go/v4` plugin the following command can be used:
+
+```sh
+kubebuilder init --domain tutorial.kubebuilder.io --repo tutorial.kubebuilder.io/project --plugins=go/v4
+```
+
+## Subcommands supported by the plugin
+
+- Init - `kubebuilder init [OPTIONS]`
+- Edit - `kubebuilder edit [OPTIONS]`
+- Create API - `kubebuilder create api [OPTIONS]`
+- Create Webhook - `kubebuilder create webhook [OPTIONS]`
+
+## Further resources
+
+- To see the composition of plugins, you can check the source code for the Kubebuilder [main.go][plugins-main].
+- Check the code implementation of the [base Golang plugin `base.go.kubebuilder.io/v4`][v4-plugin].
+- Check the code implementation of the [Kustomize/v2 plugin][kustomize-plugin].
+- Check [controller-runtime][controller-runtime] to know more about controllers.
+
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[quickstart]: ./../../quick-start.md
+[testdata]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata
+[plugins-main]: ./../../../../../cmd/main.go
+[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
+[migration-guide-doc]: ./../../migration/migration_guide_gov3_to_gov4.md
+[project-doc]: ./../../reference/project-config.md
+[bundle]: ./../../../../../pkg/plugin/bundle.go
diff --git a/docs/book/src/plugins/available/grafana-v1-alpha.md b/docs/book/src/plugins/available/grafana-v1-alpha.md
new file mode 100644
index 00000000000..d086fe17f09
--- /dev/null
+++ b/docs/book/src/plugins/available/grafana-v1-alpha.md
@@ -0,0 +1,256 @@
+# Grafana Plugin (`grafana/v1-alpha`)
+
+The Grafana plugin is an optional plugin that can be used to
+scaffold Grafana Dashboards to allow you to check out the
+default metrics which are exported by projects
+using [controller-runtime][controller-runtime].
+
+
+Examples
+
+You can check its default scaffold by looking at the `project-v4-with-plugins` projects
+under the [testdata][testdata] directory on the root directory of the Kubebuilder project.
+
+
+
+## When to use it ?
+
+- If you are looking to observe the metrics
+exported by [controller metrics][controller-metrics] and
+collected by Prometheus via [Grafana][grafana].
+
+## How to use it ?
+
+### Prerequisites:
+
+- Your project must be using [controller-runtime][controller-runtime] to expose the metrics via the [controller default metrics][controller-metrics] and they need to be collected by Prometheus.
+- Access to [Prometheus][prometheus].
+ - Prometheus should have an endpoint exposed. (For `prometheus-operator`, this is similar as: http://prometheus-k8s.monitoring.svc:9090 )
+ - The endpoint is ready to/already become the datasource of your Grafana. See [Add a data source](https://grafana.com/docs/grafana/latest/datasources/add-a-data-source/)
+- Access to [Grafana][grafana-install]. Make sure you have:
+ - [Dashboard edit permission][grafana-permissions]
+ - Prometheus Data source
+ ![pre][prometheus-data-source]
+
+
+
+Check the [metrics][reference-metrics-doc] to know how to enable the metrics for your projects scaffold with Kubebuilder.
+
+See that in the [config/prometheus][kustomize-plugin] you will find the ServiceMonitor to enable the metrics in the default endpoint `/metrics`.
+
+
+
+### Basic Usage
+
+The Grafana plugin is attached to the `init` subcommand and the `edit` subcommand:
+
+```sh
+# Initialize a new project with grafana plugin
+kubebuilder init --plugins grafana.kubebuilder.io/v1-alpha
+
+# Enable grafana plugin to an existing project
+kubebuilder edit --plugins grafana.kubebuilder.io/v1-alpha
+```
+
+The plugin will create a new directory and scaffold the JSON files under it (i.e. `grafana/controller-runtime-metrics.json`).
+
+#### Show case:
+
+See an example of how to use the plugin in your project:
+
+
+
+#### Now, let's check how to use the Grafana dashboards
+
+1. Copy the JSON file
+2. Visit `/dashboard/import` to [import a new dashboard](https://grafana.com/docs/grafana/latest/dashboards/export-import/#import-dashboard).
+3. Paste the JSON content to `Import via panel json`, then press `Load` button
+
+4. Select the data source for Prometheus metrics
+
+5. Once the json is imported in Grafana, the dashboard is ready.
+
+### Grafana Dashboard
+
+#### Controller Runtime Reconciliation total & errors
+
+- Metrics:
+ - controller_runtime_reconcile_total
+ - controller_runtime_reconcile_errors_total
+- Query:
+ - sum(rate(controller_runtime_reconcile_total{job="$job"}[5m])) by (instance, pod)
+ - sum(rate(controller_runtime_reconcile_errors_total{job="$job"}[5m])) by (instance, pod)
+- Description:
+ - Per-second rate of total reconciliation as measured over the last 5 minutes
+ - Per-second rate of reconciliation errors as measured over the last 5 minutes
+- Sample:
+
+#### Controller CPU & Memory Usage
+
+- Metrics:
+ - process_cpu_seconds_total
+ - process_resident_memory_bytes
+- Query:
+ - rate(process_cpu_seconds_total{job="$job", namespace="$namespace", pod="$pod"}[5m]) \* 100
+ - process_resident_memory_bytes{job="$job", namespace="$namespace", pod="$pod"}
+- Description:
+ - Per-second rate of CPU usage as measured over the last 5 minutes
+ - Allocated Memory for the running controller
+- Sample:
+
+#### Seconds of P50/90/99 Items Stay in Work Queue
+
+- Metrics
+ - workqueue_queue_duration_seconds_bucket
+- Query:
+ - histogram_quantile(0.50, sum(rate(workqueue_queue_duration_seconds_bucket{job="$job", namespace="$namespace"}[5m])) by (instance, name, le))
+- Description
+ - Seconds an item stays in workqueue before being requested.
+- Sample:
+
+#### Seconds of P50/90/99 Items Processed in Work Queue
+
+- Metrics
+ - workqueue_work_duration_seconds_bucket
+- Query:
+ - histogram_quantile(0.50, sum(rate(workqueue_work_duration_seconds_bucket{job="$job", namespace="$namespace"}[5m])) by (instance, name, le))
+- Description
+ - Seconds of processing an item from workqueue takes.
+- Sample:
+
+#### Add Rate in Work Queue
+
+- Metrics
+ - workqueue_adds_total
+- Query:
+ - sum(rate(workqueue_adds_total{job="$job", namespace="$namespace"}[5m])) by (instance, name)
+- Description
+ - Per-second rate of items added to work queue
+- Sample:
+
+#### Retries Rate in Work Queue
+
+- Metrics
+ - workqueue_retries_total
+- Query:
+ - sum(rate(workqueue_retries_total{job="$job", namespace="$namespace"}[5m])) by (instance, name)
+- Description
+ - Per-second rate of retries handled by workqueue
+- Sample:
+
+#### Number of Workers in Use
+
+- Metrics
+ - controller_runtime_active_workers
+- Query:
+ - controller_runtime_active_workers{job="$job", namespace="$namespace"}
+- Description
+ - The number of active controller workers
+- Sample:
+
+#### WorkQueue Depth
+
+- Metrics
+ - workqueue_depth
+- Query:
+ - workqueue_depth{job="$job", namespace="$namespace"}
+- Description
+ - Current depth of workqueue
+- Sample:
+
+#### Unfinished Seconds
+
+- Metrics
+ - workqueue_unfinished_work_seconds
+- Query:
+ - rate(workqueue_unfinished_work_seconds{job="$job", namespace="$namespace"}[5m])
+- Description
+ - How many seconds of work has done that is in progress and hasn't been observed by work_duration.
+- Sample:
+
+### Visualize Custom Metrics
+
+The Grafana plugin supports scaffolding manifests for custom metrics.
+
+#### Generate Config Template
+
+When the plugin is triggered for the first time, `grafana/custom-metrics/config.yaml` is generated.
+
+```yaml
+---
+customMetrics:
+# - metric: # Raw custom metric (required)
+# type: # Metric type: counter/gauge/histogram (required)
+# expr: # Prom_ql for the metric (optional)
+# unit: # Unit of measurement, examples: s,none,bytes,percent,etc. (optional)
+```
+
+#### Add Custom Metrics to Config
+
+You can enter multiple custom metrics in the file. For each element, you need to specify the `metric` and its `type`.
+The Grafana plugin can automatically generate `expr` for visualization.
+Alternatively, you can provide `expr` and the plugin will use the specified one directly.
+
+```yaml
+---
+customMetrics:
+ - metric: memcached_operator_reconcile_total # Raw custom metric (required)
+ type: counter # Metric type: counter/gauge/histogram (required)
+ unit: none
+ - metric: memcached_operator_reconcile_time_seconds_bucket
+ type: histogram
+```
+
+#### Scaffold Manifest
+
+Once `config.yaml` is configured, you can run `kubebuilder edit --plugins grafana.kubebuilder.io/v1-alpha` again.
+This time, the plugin will generate `grafana/custom-metrics/custom-metrics-dashboard.json`, which can be imported to Grafana UI.
+
+#### Show case:
+
+See an example of how to visualize your custom metrics:
+
+![output2][show-case]
+
+## Subcommands
+
+The Grafana 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:
+
+- `grafana/*.json`
+
+## Further resources
+
+- Check out [video to show how it works][video]
+- Checkout the [video to show how the custom metrics feature works][video-custom-metrics]
+- Refer to a sample of `serviceMonitor` provided by [kustomize plugin][kustomize-plugin]
+- Check the [plugin implementation][plugin-implementation]
+- [Grafana Docs][grafana-docs] of importing JSON file
+- The usage of serviceMonitor by [Prometheus Operator][servicemonitor]
+
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[grafana]: https://grafana.com/docs/grafana/next/
+[grafana-docs]: https://grafana.com/docs/grafana/latest/dashboards/export-import/#import-dashboard
+[kube-prometheus]: https://github.com/prometheus-operator/kube-prometheus
+[prometheus]: https://prometheus.io/docs/introduction/overview/
+[prom-operator]: https://prometheus-operator.dev/docs/prologue/introduction/
+[servicemonitor]: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/getting-started.md#related-resources
+[grafana-install]: https://grafana.com/docs/grafana/latest/setup-grafana/installation/
+[grafana-permissions]: https://grafana.com/docs/grafana/next/administration/roles-and-permissions/#dashboard-permissions
+[prometheus-data-source]: https://user-images.githubusercontent.com/18136486/176119794-f6d69b0b-93f0-4f9e-a53c-daf9f77dadae.gif
+[video]: https://youtu.be/-w_JjcV8jXc
+[video-custom-metrics]: https://youtu.be/x_0FHta2HXc
+[show-case]: https://user-images.githubusercontent.com/18136486/186933170-d2e0de71-e079-4d1b-906a-99a549d66ebf.gif
+[controller-metrics]: ./../../reference/metrics-reference.md
+[kustomize-plugin]: ./../../../../../testdata/project-v4-with-plugins/config/prometheus/monitor.yaml
+[plugin-implementation]: ./../../../../../pkg/plugins/optional/grafana/
+[reference-metrics-doc]: ./../../reference/metrics.md#exporting-metrics-for-prometheus
+[testdata]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata/project-v4-with-plugins
+
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..a27f3ea8b72
--- /dev/null
+++ b/docs/book/src/plugins/available/helm-v1-alpha.md
@@ -0,0 +1,129 @@
+# Helm Plugin (`helm/v1-alpha`) - **DEPRECATED**
+
+
+ Deprecated Plugin
+
+The `helm/v1-alpha` plugin is **deprecated**. Please use [`helm/v2-alpha`](./helm-v2-alpha.md) instead.
+
+The v2-alpha version provides:
+- Dynamic Helm chart generation from kustomize output
+- Better preservation of customizations (env vars, labels, annotations)
+- Organized template structure matching your config/ directory
+- More flexible configuration options
+
+
+
+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.
+
+
+Examples
+
+You can check the plugin usage by looking at `project-v4-with-plugins` samples
+under the [testdata][testdata] directory on the root directory of the Kubebuilder project.
+
+
+
+## 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.
+
+
+ Why CRDs are added under templates?
+
+Although [Helm best practices](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you) recommend placing CRDs under a top-level `crds/` directory, the Kubebuilder Helm plugin intentionally places them under `templates/crd`.
+
+The rationale is tied to how Helm itself handles CRDs.
+By default, Helm will install CRDs once during the initial release,
+but it will **ignore CRD changes** on subsequent upgrades.
+
+This can lead to surprising behavior where chart upgrades silently
+skip CRD updates, leaving clusters out of sync.
+
+To avoid endorsing this behavior, the Kubebuilder plugin follows the approach of packaging
+CRDs inside `templates/`. In this mode, Helm treats CRDs like
+any other resource, ensuring they are applied and upgraded as expected.
+While this prevents mixing CRDs and CRs of the same type in a single chart (since Helm cannot wait between creation steps), it ensures predictable and explicit lifecycle management of CRDs.
+
+In short:
+- **Helm `crds/` directory** → one-time install only, no upgrades.
+- **Kubebuilder `templates/crd`** → CRDs managed like other manifests, upgrades included.
+
+This design choice prioritizes correctness and maintainability over Helm's default convention,
+while leaving room for future improvements (such as scaffolding separate charts for APIs and controllers).
+
+
+## How to use it ?
+
+### Basic Usage
+
+The Helm plugin is attached to the `edit` subcommand as the `helm/v1-alpha` plugin
+relies on the Go project being scaffolded first.
+
+```sh
+
+# Initialize a new project
+kubebuilder init
+
+# 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]`)
+
+## 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/available/helm-v2-alpha.md b/docs/book/src/plugins/available/helm-v2-alpha.md
new file mode 100644
index 00000000000..16d236b1579
--- /dev/null
+++ b/docs/book/src/plugins/available/helm-v2-alpha.md
@@ -0,0 +1,248 @@
+# Helm Plugin `(helm/v2-alpha)`
+
+The Helm plugin **v2-alpha** provides a way to package your project as a Helm chart, enabling distribution in Helm’s native format.
+Instead of using static templates, this plugin dynamically generates Helm charts from your project’s **kustomize output** (via `make build-installer`).
+It keeps your custom settings such as environment variables, labels, annotations, and security contexts.
+
+This lets you deliver your Kubebuilder project in two ways:
+- As a **bundle** (`dist/install.yaml`) generated with kustomize
+- As a **Helm chart** that matches the same output
+
+## Why Helm?
+
+By default, you can create a bundle of manifests with:
+
+```shell
+make build-installer IMG=/
+```
+
+Users can install it directly:
+
+```shell
+kubectl apply -f https://raw.githubusercontent.com//project-v4//dist/install.yaml
+```
+But many people prefer Helm for packaging, upgrades, and distribution.
+The **helm/v2-alpha** plugin converts the bundle (`dist/install.yaml`) into a Helm chart that mirrors your project.
+
+## Key Features
+
+- **Dynamic Generation**: Charts are built from real kustomize output, not boilerplate.
+- **Preserves Customizations**: Keeps env vars, labels, annotations, and patches.
+- **Structured Output**: Templates follow your `config/` directory layout.
+- **Smart Values**: `values.yaml` includes only actual configurable parameters.
+- **File Preservation**: Manual edits in `values.yaml`, `Chart.yaml`, `_helpers.tpl` are kept unless `--force` is used.
+
+## When to Use It
+
+Use the **helm/v2-alpha** plugin if:
+- You want Helm charts that stay true to your kustomize setup
+- You need charts that update with your project automatically
+- You want a clean template layout similar to `config/`
+- You want to distribute your solution using either this format
+
+## Usage
+
+### Basic Workflow
+
+```shell
+# Create a new project
+kubebuilder init
+
+# Build the installer bundle
+make build-installer IMG=/
+
+# Create Helm chart from kustomize output
+kubebuilder edit --plugins=helm/v2-alpha
+
+# Overwrite preserved files if needed
+kubebuilder edit --plugins=helm/v2-alpha --force
+```
+
+### Advanced Options
+
+```shell
+# Use a custom manifests file
+kubebuilder edit --plugins=helm/v2-alpha --manifests=manifests/custom-install.yaml
+
+# Write chart to a custom output directory
+kubebuilder edit --plugins=helm/v2-alpha --output-dir=charts
+
+# Combine manifests and output
+kubebuilder edit --plugins=helm/v2-alpha \
+ --manifests=manifests/install.yaml \
+ --output-dir=helm-charts
+```
+
+## Chart Structure
+
+The plugin creates a chart layout that matches your `config/`:
+
+```shell
+/chart/
+├── Chart.yaml
+├── values.yaml
+├── .helmignore
+└── templates/
+ ├── _helpers.tpl
+ ├── rbac/ # Individual RBAC files
+ │ ├── controller-manager.yaml
+ │ ├── leader-election-role.yaml
+ │ ├── leader-election-rolebinding.yaml
+ │ ├── manager-role.yaml
+ │ ├── manager-rolebinding.yaml
+ │ ├── metrics-auth-role.yaml
+ │ ├── metrics-auth-rolebinding.yaml
+ │ ├── metrics-reader.yaml
+ │ ├── memcached-admin-role.yaml
+ │ ├── memcached-editor-role.yaml
+ │ ├── memcached-viewer-role.yaml
+ │ └── ...
+ ├── crd/ # Individual CRD files
+ │ ├── memcacheds.example.com.testproject.org.yaml
+ │ ├── busyboxes.example.com.testproject.org.yaml
+ │ ├── wordpresses.example.com.testproject.org.yaml
+ │ └── ...
+ ├── cert-manager/
+ │ ├── metrics-certs.yaml
+ │ ├── serving-cert.yaml
+ │ └── selfsigned-issuer.yaml
+ ├── manager/
+ │ └── manager.yaml
+ ├── service/
+ │ └── service.yaml
+ ├── webhook/
+ │ └── validating-webhook-configuration.yaml
+ └── prometheus/
+ └── servicemonitor.yaml
+```
+
+
+ Why CRDs are added under templates?
+
+Although [Helm best practices](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you) recommend placing CRDs under a top-level `crds/` directory, the Kubebuilder Helm plugin intentionally places them under `templates/crd`.
+
+The rationale is tied to how Helm itself handles CRDs.
+By default, Helm will install CRDs once during the initial release,
+but it will **ignore CRD changes** on subsequent upgrades.
+
+This can lead to surprising behavior where chart upgrades silently
+skip CRD updates, leaving clusters out of sync.
+
+To avoid endorsing this behavior, the Kubebuilder plugin follows the approach of packaging
+CRDs inside `templates/`. In this mode, Helm treats CRDs like
+any other resource, ensuring they are applied and upgraded as expected.
+While this prevents mixing CRDs and CRs of the same type in a single chart (since Helm cannot wait between creation steps), it ensures predictable and explicit lifecycle management of CRDs.
+
+In short:
+- **Helm `crds/` directory** → one-time install only, no upgrades.
+- **Kubebuilder `templates/crd`** → CRDs managed like other manifests, upgrades included.
+
+This design choice prioritizes correctness and maintainability over Helm's default convention,
+while leaving room for future improvements (such as scaffolding separate charts for APIs and controllers).
+
+
+## Values Configuration
+
+The generated `values.yaml` provides configuration options extracted from your actual deployment.
+Namespace creation is not managed by the chart; use Helm's `--namespace` and `--create-namespace` flags when installing.
+
+**Example**
+
+```yaml
+# Configure the controller manager deployment
+controllerManager:
+ replicas: 1
+
+ image:
+ repository: controller
+ tag: latest
+ pullPolicy: IfNotPresent
+
+ # Environment variables from your deployment
+ env:
+ - name: BUSYBOX_IMAGE
+ value: busybox:1.36.1
+ - name: MEMCACHED_IMAGE
+ value: memcached:1.6.26-alpine3.19
+
+ # Pod-level security settings
+ podSecurityContext:
+ runAsNonRoot: true
+ seccompProfile:
+ type: RuntimeDefault
+
+ # Container-level security settings
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ readOnlyRootFilesystem: true
+
+ # Resource limits and requests
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+
+# Essential RBAC permissions (required for controller operation)
+# These include ServiceAccount, controller permissions, leader election, and metrics access
+# Note: Essential RBAC is always enabled as it's required for the controller to function
+
+# Extra labels applied to all rendered manifests
+commonLabels: {}
+
+# Helper RBAC roles for managing custom resources
+# These provide convenient admin/editor/viewer roles for each CRD type
+# Useful for giving users different levels of access to your custom resources
+rbacHelpers:
+ enable: false # Install convenience admin/editor/viewer roles for CRDs
+
+# Custom Resource Definitions
+crd:
+ enable: true # Install CRDs with the chart
+ keep: true # Keep CRDs when uninstalling
+
+# Controller metrics endpoint.
+# Enable to expose /metrics endpoint with RBAC protection.
+metrics:
+ enable: true
+
+# Cert-manager integration for TLS certificates.
+# Required for webhook certificates and metrics endpoint certificates.
+certManager:
+ enable: true
+
+# Prometheus ServiceMonitor for metrics scraping.
+# Requires prometheus-operator to be installed in the cluster.
+prometheus:
+ enable: false
+```
+
+### Installation Tip
+
+Install the chart into a namespace using Helm flags (the chart does not create namespaces):
+
+```shell
+helm install my-release ./dist/chart \
+ --namespace my-project-system \
+ --create-namespace
+```
+
+## Flags
+
+| Flag | Description |
+|---------------------|-----------------------------------------------------------------------------|
+| **--manifests** | Path to YAML file containing Kubernetes manifests (default: `dist/install.yaml`) |
+| **--output-dir** string | Output directory for chart (default: `dist`) |
+| **--force** | Overwrites preserved files (`values.yaml`, `Chart.yaml`, `_helpers.tpl`) |
+
+
+ Examples
+
+You can find example projects in [testdata/project-v4-with-plugins](https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata/project-v4-with-plugins).
+
+
diff --git a/docs/book/src/plugins/available/kustomize-v2.md b/docs/book/src/plugins/available/kustomize-v2.md
new file mode 100644
index 00000000000..929b96457ed
--- /dev/null
+++ b/docs/book/src/plugins/available/kustomize-v2.md
@@ -0,0 +1,105 @@
+# Kustomize v2
+
+**(Default Scaffold)**
+
+The Kustomize plugin allows you to scaffold all kustomize manifests used
+with the language base plugin `base.go.kubebuilder.io/v4`.
+This plugin is used to generate the manifest under the `config/` directory
+for projects built within the `go/v4` plugin (default scaffold).
+
+Projects like [Operator-sdk][sdk] use the Kubebuilder project as a library
+and provide options for working with other languages such as Ansible and Helm.
+The Kustomize plugin helps them maintain consistent configuration across
+languages. It also simplifies the creation of plugins that perform
+changes on top of the default scaffold, removing the need for manual
+updates across multiple language plugins. This approach allows the
+creation of "helper" plugins that work with different projects
+and languages.
+
+
+Examples
+
+You can check the kustomize content by looking at the `config/` directory provided in the sample `project-v4-*`
+under the [testdata][testdata] directory of the Kubebuilder project.
+
+
+
+## How to use it
+
+If you want your language plugin to use kustomize, use the [Bundle Plugin][bundle] to specify that your language plugin is composed of your language-specific plugin and kustomize for its configuration, as shown:
+
+```go
+import (
+ ...
+ kustomizecommonv2 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/common/kustomize/v2"
+ golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
+ ...
+)
+
+// Bundle plugin for Golang projects scaffolded by Kubebuilder go/v4
+gov4Bundle, _ := plugin.NewBundle(plugin.WithName(golang.DefaultNameQualifier),
+ plugin.WithVersion(plugin.Version{Number: 4}),
+ plugin.WithPlugins(kustomizecommonv2.Plugin{}, golangv4.Plugin{}), // Scaffold the config/ directory and all kustomize files
+)
+```
+
+You can also use kustomize/v2 alone via:
+
+```sh
+kubebuilder init --plugins=kustomize/v2
+$ ls -la
+total 24
+drwxr-xr-x 6 camilamacedo86 staff 192 31 Mar 09:56 .
+drwxr-xr-x 11 camilamacedo86 staff 352 29 Mar 21:23 ..
+-rw------- 1 camilamacedo86 staff 129 26 Mar 12:01 .dockerignore
+-rw------- 1 camilamacedo86 staff 367 26 Mar 12:01 .gitignore
+-rw------- 1 camilamacedo86 staff 94 31 Mar 09:56 PROJECT
+drwx------ 6 camilamacedo86 staff 192 31 Mar 09:56 config
+```
+
+Or combined with the base language plugins:
+
+```sh
+# Provides the same scaffold of go/v4 plugin which is composition but with kustomize/v2
+kubebuilder init --plugins=kustomize/v2,base.go.kubebuilder.io/v4 --domain example.org --repo example.org/guestbook-operator
+```
+
+## Subcommands
+
+The kustomize plugin implements the following subcommands:
+
+* init (`$ kubebuilder init [OPTIONS]`)
+* create api (`$ kubebuilder create api [OPTIONS]`)
+* create webhook (`$ kubebuilder create api [OPTIONS]`)
+
+
+Create API and Webhook
+
+The implementation for the `create api` subcommand scaffolds the kustomize
+manifests specific to each API. See more [here][kustomize-create-api].
+The same applies to `create webhook`.
+
+
+
+## Affected files
+
+The following scaffolds will be created or updated by this plugin:
+
+* `config/*`
+
+## Further resources
+
+* Check the kustomize [plugin implementation](https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/plugins/common/kustomize)
+* Check the [kustomize documentation][kustomize-docs]
+* Check the [kustomize repository][kustomize-github]
+
+[sdk]:https://github.com/operator-framework/operator-sdk
+[kustomize-docs]: https://kustomize.io/
+[kustomize-github]: https://github.com/kubernetes-sigs/kustomize
+[kustomize-replacements]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/replacements/
+[kustomize-vars]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/vars/
+[release-notes-v5]: https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv5.0.0
+[release-notes-v4]: https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv4.0.0
+[testdata]: ./../../../../../testdata/
+[bundle]: ./../../../../../pkg/plugin/bundle.go
+[kustomize-create-api]: ./../../../../../pkg/plugins/common/kustomize/v2/scaffolds/api.go
diff --git a/docs/book/src/plugins/creating-plugins.md b/docs/book/src/plugins/creating-plugins.md
deleted file mode 100644
index 2c599b87a19..00000000000
--- a/docs/book/src/plugins/creating-plugins.md
+++ /dev/null
@@ -1,78 +0,0 @@
-# Creating your own plugins
-
-## Overview
-
-You can extend the Kubebuilder API to create your own plugins. If [extending the CLI][extending-cli], your plugin will be implemented in your project and registered to the CLI as has been done by the [SDK][sdk] project. See its [cli code][sdk-cli-pkg] as an example.
-
-## Language-based Plugins
-
-Kubebuilder offers the Golang-based operator plugins, which will help its CLI tool users create projects following the [Operator Pattern][operator-pattern].
-
-The [SDK][sdk] project, for example, has language plugins for [Ansible][sdk-ansible] and [Helm][sdk-helm], which are similar options but for users who would like to work with these respective languages and stacks instead of Golang.
-
-Note that Kubebuilder provides the `kustomize.common.kubebuilder.io` to help in these efforts. This plugin will scaffold the common base without any specific language scaffold file to allow you to extend the Kubebuilder style for your plugins.
-
-In this way, currently, you can [Extend the CLI][extending-cli] and use the `Bundle Plugin` to create your language plugins such as:
-
-```go
- mylanguagev1Bundle, _ := plugin.NewBundle(language.DefaultNameQualifier, plugin.Version{Number: 1},
- kustomizecommonv1.Plugin{}, // extend the common base from Kuebebuilder
- mylanguagev1.Plugin{}, // your plugin language which will do the scaffolds for the specific language on top of the common base
- )
-```
-
-If you do not want to develop your plugin using Golang, you can follow its standard by using the binary as follows:
-
-```sh
-kubebuilder init --plugins=kustomize
-```
-
-Then you can, for example, create your implementations for the sub-commands `create api` and `create webhook` using your language of preference.
-
-
-Why use the Kubebuilder style?
-
-Kubebuilder and SDK are both broadly adopted projects which leverage the [controller-runtime][controller-runtime] project. They both allow users to build solutions using the [Operator Pattern][operator-pattern] and follow common standards.
-
-Adopting these standards can bring significant benefits, such as joining forces on maintaining the common standards as the features provided by Kubebuilder and take advantage of the contributions made by the community. This allows you to focus on the specific needs and requirements for your plugin and use-case.
-
-And then, you will also be able to use custom plugins and options currently or in the future which might to be provided by these projects as any other which decides to persuade the same standards.
-
-
-
-## Custom Plugins
-
-Note that users are also able to use plugins to customize their scaffold and address specific needs. See that Kubebuilder provides the [declarative][declarative-code] plugin which can be used when for example an API is scaffold:
-
-```sh
-kubebuider create api [options] --plugins=go/v3,declarative/v1
-```
-
-This plugin will perform a custom scaffold using the [kubebuilder declarative pattern][kubebuilder-declarative-pattern].
-
-In this way, by [Extending the Kubebuilder CLI][extending-cli], you can also create custom plugins such this one. Feel free to check its implementation in [`pkg/plugins/golang/declarative`][declarative-code].
-
-## Future vision for Kubebuilder Plugins
-
-As the next steps for the plugins, its possible to highlight three initiatives so far, which are:
-
-- [Plugin phase 2.0][plugin-2.0]: allow the Kubebuilder CLI or any other CLI, which is [Extending the Kubebuilder CLI][extending-cli], to discover external plugins, in this way, allow the users to use these external options as helpers to perform the scaffolds with the tool.
-- [Config-gen][config-gen]: the config-gen option has been provided as an alpha option in the Kubebuilder CLI(`kubebuilder alpha config-gen`) to encourage its contributions. The idea of this option would simplify the config scaffold. For further information see its [README][config-gen-readme].
-- [New Plugin (`deploy-image.go.kubebuilder.io/v1beta1`) to generate code][new-plugin-gen]: its purpose is to provide an arch-type that will scaffold the APIs and Controllers with the required code to deploy and manage solutions on the cluster.
-
-Please, feel to contribute with them as well. Your contribution to the project is very welcome.
-
-[sdk-cli-pkg]: https://github.com/operator-framework/operator-sdk/blob/master/internal/cmd/operator-sdk/cli/cli.go
-[sdk-ansible]: https://github.com/operator-framework/operator-sdk/tree/master/internal/plugins/ansible/v1
-[sdk-helm]: https://github.com/operator-framework/operator-sdk/tree/master/internal/plugins/helm/v1
-[operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
-[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
-[plugin-2.0]: https://github.com/kubernetes-sigs/kubebuilder/issues/1378
-[config-gen-readme]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/cli/alpha/config-gen/README.md
-[config-gen]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/cli/alpha/config-gen
-[plugins-phase1-design-doc-1.5]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1-5.md
-[extending-cli]: extending-cli.md
-[new-plugin-gen]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/code-generate-image-plugin.md
-[kubebuilder-declarative-pattern]: https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern
-[declarative-code]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/golang/declarative
-[sdk]: https://github.com/operator-framework/operator-sdk
\ No newline at end of file
diff --git a/docs/book/src/plugins/extending-cli.md b/docs/book/src/plugins/extending-cli.md
deleted file mode 100644
index 953f98c99e6..00000000000
--- a/docs/book/src/plugins/extending-cli.md
+++ /dev/null
@@ -1,213 +0,0 @@
-# Extending the CLI and Scaffolds
-
-## Overview
-
-You can extend Kubebuilder to allow your project to have the same CLI features and provide the plugins scaffolds.
-
-## CLI system
-
-Plugins are run using a [`CLI`][cli] object, which maps a plugin type to a subcommand and calls that plugin's methods.
-For example, writing a program that injects an `Init` plugin into a `CLI` then calling `CLI.Run()` will call the
-plugin's [SubcommandMetadata][plugin-sub-command], [UpdatesMetadata][plugin-update-meta] and `Run` methods with information a user has passed to the
-program in `kubebuilder init`. Following an example:
-
-```go
-package cli
-
-import (
- log "github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
-
- "sigs.k8s.io/kubebuilder/v3/pkg/cli"
- cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3"
- "sigs.k8s.io/kubebuilder/v3/pkg/plugin"
- kustomizecommonv1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/common/kustomize/v1"
- "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang"
- declarativev1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1"
- golangv3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3"
-
-)
-
-var (
- // The following is an example of the commands
- // that you might have in your own binary
- commands = []*cobra.Command{
- myExampleCommand.NewCmd(),
- }
- alphaCommands = []*cobra.Command{
- myExampleAlphaCommand.NewCmd(),
- }
-)
-
-// GetPluginsCLI returns the plugins based CLI configured to be used in your CLI binary
-func GetPluginsCLI() (*cli.CLI) {
- // Bundle plugin which built the golang projects scaffold by Kubebuilder go/v3
- gov3Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, plugin.Version{Number: 3},
- kustomizecommonv1.Plugin{},
- golangv3.Plugin{},
- )
-
-
- c, err := cli.New(
- // Add the name of your CLI binary
- cli.WithCommandName("example-cli"),
-
- // Add the version of your CLI binary
- cli.WithVersion(versionString()),
-
- // Register the plugins options which can be used to do the scaffolds via your CLI tool. See that we are using as example here the plugins which are implemented and provided by Kubebuilder
- cli.WithPlugins(
- gov3Bundle,
- &declarativev1.Plugin{},
- ),
-
- // Defines what will be the default plugin used by your binary. It means that will be the plugin used if no info be provided such as when the user runs `kubebuilder init`
- cli.WithDefaultPlugins(cfgv3.Version, gov3Bundle),
-
- // Define the default project configuration version which will be used by the CLI when none is informed by --project-version flag.
- cli.WithDefaultProjectVersion(cfgv3.Version),
-
- // Adds your own commands to the CLI
- cli.WithExtraCommands(commands...),
-
- // Add your own alpha commands to the CLI
- cli.WithExtraAlphaCommands(alphaCommands...),
-
- // Adds the completion option for your CLI
- cli.WithCompletion(),
- )
- if err != nil {
- log.Fatal(err)
- }
-
- return c
-}
-
-// versionString returns the CLI version
-func versionString() string {
- // return your binary project version
-}
-```
-
-This program can then be built and run in the following ways:
-
-Default behavior:
-
-```sh
-# Initialize a project with the default Init plugin, "go.example.com/v1".
-# This key is automatically written to a PROJECT config file.
-$ my-bin-builder init
-# Create an API and webhook with "go.example.com/v1" CreateAPI and
-# CreateWebhook plugin methods. This key was read from the config file.
-$ my-bin-builder create api [flags]
-$ my-bin-builder create webhook [flags]
-```
-
-Selecting a plugin using `--plugins`:
-
-```sh
-# Initialize a project with the "ansible.example.com/v1" Init plugin.
-# Like above, this key is written to a config file.
-$ my-bin-builder init --plugins ansible
-# Create an API and webhook with "ansible.example.com/v1" CreateAPI
-# and CreateWebhook plugin methods. This key was read from the config file.
-$ my-bin-builder create api [flags]
-$ my-bin-builder create webhook [flags]
-```
-
-### CLI manages the PROJECT file
-
-The CLI is responsible for managing the [PROJECT file config][project-file-config], representing the configuration of the projects that are scaffold by the CLI tool.
-
-## Plugins
-
-Kubebuilder provides scaffolding options via plugins. 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 preserve the default help text set by the [cobra][cobra] command constructors.
-
-Kubebuilder CLI plugins wrap scaffolding and CLI features in conveniently packaged Go types that are executed by the
-`kubebuilder` binary, or any binary which imports them. More specifically, a plugin configures the execution of one
-of the following CLI commands:
-
-- `init`: project initialization.
-- `create api`: scaffold Kubernetes API definitions.
-- `create webhook`: scaffold Kubernetes webhooks.
-
-Plugins are identified by a key of the form `/`. 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.
-
-By default, `` will be `go.kubebuilder.io/vX`, where `X` is some integer.
-
-For a full implementation example, check out Kubebuilder's native [`go.kubebuilder.io`][kb-go-plugin] plugin.
-
-### Plugin naming
-
-Plugin names must be DNS1123 labels and should be fully qualified, i.e. they have a suffix like
-`.example.com`. For example, the base Go scaffold used with `kubebuilder` commands has name `go.kubebuilder.io`.
-Qualified names prevent conflicts between plugin names; both `go.kubebuilder.io` and `go.example.com` can both scaffold
-Go code and can be specified by a user.
-
-### Plugin versioning
-
-A plugin's `Version()` method returns a [`plugin.Version`][plugin-version-type] object containing an integer value
-and optionally a stage string of either "alpha" or "beta". The integer denotes the current version of a plugin.
-Two different integer values between versions of plugins indicate that the two plugins are incompatible. The stage
-string denotes plugin stability:
-
-- `alpha`: should be used for plugins that are frequently changed and may break between uses.
-- `beta`: should be used for plugins that are only changed in minor ways, ex. bug fixes.
-
-### Breaking changes
-
-Any change that will break a project scaffolded by the previous plugin version is a breaking change.
-
-### Plugins Deprecation
-
-Once a plugin is deprecated, have it implement a [Deprecated][deprecate-plugin-doc] interface so a deprecation warning will be printed when it is used.
-
-## Bundle Plugins
-
-[Bundle Plugins][bundle-plugin-doc] allow you to create a plugin that is a composition of many plugins:
-
-```go
- // see that will be like myplugin.example/v1`
- myPluginBundle, _ := plugin.NewBundle(``,``,
- pluginA.Plugin{},
- pluginB.Plugin{},
- pluginC.Plugin{},
- )
-
-```
-
-Note that it means that when a user of your CLI calls this plugin, the execution of the sub-commands will be sorted by the order to which they were added in a chain:
-
-
-> sub-command of plugin A -> sub-command of plugin B -> sub-command of plugin C
-
-Then, to initialize using this "Plugin Bundle" which will run the chain of plugins:
-
-```
-kubebuider init --plugins=myplugin.example/v1
-```
-
-- Runs init sub-command of the plugin A
-- And then, runs init sub-command of the plugin B
-- And then, runs init sub-command of the plugin C
-
-[project-file-config]: ../reference/project-config.md
-[plugin-interface]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Plugin
-[go-dev-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3
-[plugin-sub-command]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Subcommand
-[project-file]: ../reference/project-config.md
-[plugin-subc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Subcommand
-[cobra]:https://pkg.go.dev/github.com/spf13/cobra
-[kb-go-plugin]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3
-[bundle-plugin-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Bundle
-[deprecate-plugin-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Deprecated
-[plugin-update-meta]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#UpdatesMetadata
-[cli]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/cli
-[plugin-version-type]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Version
\ No newline at end of file
diff --git a/docs/book/src/plugins/extending.md b/docs/book/src/plugins/extending.md
new file mode 100644
index 00000000000..fb5b5e75aea
--- /dev/null
+++ b/docs/book/src/plugins/extending.md
@@ -0,0 +1,57 @@
+# Extending Kubebuilder
+
+Kubebuilder provides an extensible architecture to scaffold
+projects using plugins. These plugins allow you to customize the CLI
+behavior or integrate new features.
+
+## Overview
+
+Kubebuilder’s CLI can be extended through custom plugins, allowing you to:
+
+- Build new scaffolds.
+- Enhance existing ones.
+- Add new commands and functionality to Kubebuilder’s scaffolding.
+
+This flexibility enables you to create custom project
+setups tailored to specific needs.
+
+
+Why use the Kubebuilder style?
+
+Kubebuilder and SDK are both broadly adopted projects which leverage the [controller-runtime][controller-runtime] project. They both allow users to build solutions using the [Operator Pattern][operator-pattern] and follow common standards.
+
+Adopting these standards can bring significant benefits, such as joining forces on maintaining the common standards as the features provided by Kubebuilder and take advantage of the contributions made by the community. This allows you to focus on the specific needs and requirements for your plugin and use-case.
+
+And then, you will also be able to use custom plugins and options currently or in the future which might to be provided by these projects as any other which decides to persuade the same standards.
+
+
+
+## Options to Extend
+
+Extending Kubebuilder can be achieved in two main ways:
+
+1. **Extending CLI features and Plugins**:
+ You can import and build upon existing Kubebuilder plugins to [extend
+ its features and plugins][extending-cli]. This is useful when you need to add specific
+ features to a tool that already benefits from Kubebuilder's scaffolding system.
+ For example, [Operator SDK][sdk] leverages the [kustomize plugin][kustomize-plugin]
+ to provide language support for tools like Ansible or Helm. So that the project
+ can be focused to keep maintained only what is specific language based.
+
+2. **Creating External Plugins**:
+ You can build standalone, independent plugins as binaries. These plugins can be written in any
+ language and should follow an execution pattern that Kubebuilder recognizes. For more information,
+ see [Creating external plugins][external-plugins].
+
+For further details on how to extend Kubebuilder, explore the following sections:
+
+- [CLI and Plugins](./extending/extending_cli_features_and_plugins.md) to learn how to extend CLI features and plugins.
+- [External Plugins](./extending/external-plugins.md) for creating standalone plugins.
+- [E2E Tests](./extending/testing-plugins.md) to ensure your plugin functions as expected.
+
+[extending-cli]: ./extending/extending_cli_features_and_plugins.md
+[external-plugins]: ./extending/external-plugins.md
+[sdk]: https://github.com/operator-framework/operator-sdk
+[kustomize-plugin]: ./available/kustomize-v2.md
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
\ 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
new file mode 100644
index 00000000000..8b117ff5581
--- /dev/null
+++ b/docs/book/src/plugins/extending/extending_cli_features_and_plugins.md
@@ -0,0 +1,412 @@
+# Extending CLI Features and Plugins
+
+Kubebuilder provides an extensible architecture to scaffold
+projects using plugins. These plugins allow you to customize the CLI
+behavior or integrate new features.
+
+In this guide, we’ll explore how to extend CLI features,
+create custom plugins, and bundle multiple plugins.
+
+## Creating Custom Plugins
+
+To create a custom plugin, you need to implement
+the [Kubebuilder Plugin interface][plugin-interface].
+
+This interface allows your plugin to hook into Kubebuilder’s
+commands (`init`, `create api`, `create webhook`, etc.)
+and add custom logic.
+
+### Example of a Custom Plugin
+
+You can create a plugin that generates both
+language-specific scaffolds and the necessary configuration files,
+using the [Bundle Plugin](#bundle-plugin). This example shows how to
+combine the Golang plugin with a Kustomize plugin:
+
+```go
+import (
+ kustomizecommonv2 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/common/kustomize/v2"
+ golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
+)
+
+mylanguagev1Bundle, _ := plugin.NewBundle(
+ plugin.WithName("mylanguage.kubebuilder.io"),
+ plugin.WithVersion(plugin.Version{Number: 1}),
+ plugin.WithPlugins(kustomizecommonv2.Plugin{}, mylanguagev1.Plugin{}),
+)
+```
+
+This composition allows you to scaffold a common
+configuration base (via Kustomize) and the
+language-specific files (via `mylanguagev1`).
+
+You can also use your plugin to scaffold specific
+resources like CRDs and controllers, using
+the `create api` and `create webhook` subcommands.
+
+### Plugin 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-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.
+
+Kubebuilder CLI plugins wrap scaffolding and CLI features in conveniently packaged Go types that are executed by the
+`kubebuilder` binary, or any binary which imports them. More specifically, a plugin configures the execution of one
+of the following CLI commands:
+
+- `init`: Initializes the project structure.
+- `create api`: Scaffolds a new API and controller.
+- `create webhook`: Scaffolds a new webhook.
+- `edit`: edit the project structure.
+
+Here’s an example of using the `init` subcommand with a custom plugin:
+
+```sh
+kubebuilder init --plugins=mylanguage.kubebuilder.io/v1
+```
+
+This would initialize a project using the `mylanguage` plugin.
+
+### Plugin Keys
+
+Plugins are identified by a key of the form `/`.
+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-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.
+
+For a full implementation example, check out Kubebuilder's native [`go.kubebuilder.io`][kb-go-plugin] plugin.
+
+### Plugin naming
+
+Plugin names must be DNS1123 labels and should be fully qualified, i.e. they have a suffix like
+`.example.com`. For example, the base Go scaffold used with `kubebuilder` commands has name `go.kubebuilder.io`.
+Qualified names prevent conflicts between plugin names; both `go.kubebuilder.io` and `go.example.com` can both scaffold
+Go code and can be specified by a user.
+
+### Plugin versioning
+
+A plugin's `Version()` method returns a [`plugin.Version`][plugin-version-type] object containing an integer value
+and optionally a stage string of either "alpha" or "beta". The integer denotes the current version of a plugin.
+Two different integer values between versions of plugins indicate that the two plugins are incompatible. The stage
+string denotes plugin stability:
+
+- `alpha`: should be used for plugins that are frequently changed and may break between uses.
+- `beta`: should be used for plugins that are only changed in minor ways, ex. bug fixes.
+
+### Boilerplates
+
+The Kubebuilder internal plugins use boilerplates to generate the
+files of code. Kubebuilder uses templating to scaffold files for plugins.
+For instance, when creating a new project, the `go/v4` plugin
+scaffolds the `go.mod` file using a template defined in
+its implementation.
+
+You can extend this functionality in your custom
+plugin by defining your own templates and using
+[Kubebuilder’s machinery library][machinery] to generate files.
+This library allows you to:
+
+- Define file I/O behaviors.
+- Add [markers][markers-scaffold] to the scaffolded files.
+- Specify templates for your scaffolds.
+
+#### Example: Boilerplate
+
+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}}
+```
+
+Such object that implements the machinery interface will later pass to the
+execution of scaffold:
+
+```go
+// Scaffold implements cmdutil.Scaffolder
+func (s *initScaffolder) Scaffold() error {
+ log.Println("Writing scaffold for you to edit...")
+
+ // Initialize the machinery.Scaffold that will write the boilerplate file to disk
+ // The boilerplate file needs to be scaffolded as a separate step as it is going to
+ // be used by the rest of the files, even those scaffolded in this command call.
+ scaffold := machinery.NewScaffold(s.fs,
+ machinery.WithConfig(s.config),
+ )
+
+ ...
+
+ return scaffold.Execute(
+ ...
+ &templates.GoMod{
+ ControllerRuntimeVersion: ControllerRuntimeVersion,
+ },
+ ...
+ )
+}
+```
+
+#### Example: Overwriting a File in a Plugin
+
+Let's imagine that when a subcommand is called, you want
+to overwrite an existing file.
+
+For example, to modify the `Makefile` and add custom build steps,
+in the definition of your Template you can use the following option:
+
+```go
+f.IfExistsAction = machinery.OverwriteFile
+```
+
+By using those options, your plugin can take control
+of certain files generated by Kubebuilder’s default scaffolds.
+
+## Customizing Existing Scaffolds
+
+Kubebuilder provides utility functions to help you modify the default scaffolds. By using the [plugin utilities][plugin-utils], you can insert, replace, or append content to files generated by Kubebuilder, giving you full control over the scaffolding process.
+
+These utilities allow you to:
+
+- **Insert content**: Add content at a specific location within a file.
+- **Replace content**: Search for and replace specific sections of a file.
+- **Append content**: Add content to the end of a file without removing or altering the existing content.
+
+### Example
+
+If you need to insert custom content into a scaffolded file,
+you can use the `InsertCode` function provided by the plugin utilities:
+
+```go
+pluginutil.InsertCode(filename, target, code)
+```
+
+This approach enables you to extend and modify the generated
+scaffolds while building custom plugins.
+
+For more details, refer to the [Kubebuilder plugin utilities][kb-utils].
+
+## Bundle Plugin
+
+Plugins can be bundled to compose more complex scaffolds.
+A plugin bundle is a composition of multiple plugins that
+are executed in a predefined order. For example:
+
+```go
+myPluginBundle, _ := plugin.NewBundle(
+ plugin.WithName("myplugin.example.com"),
+ plugin.WithVersion(plugin.Version{Number: 1}),
+ plugin.WithPlugins(pluginA.Plugin{}, pluginB.Plugin{}, pluginC.Plugin{}),
+)
+```
+
+This bundle will execute the `init` subcommand for each
+plugin in the specified order:
+
+1. `pluginA`
+2. `pluginB`
+3. `pluginC`
+
+The following command will run the bundled plugins:
+
+```sh
+kubebuilder init --plugins=myplugin.example.com/v1
+```
+
+## CLI system
+
+Plugins are run using a [`CLI`][cli] object, which maps a plugin type to a subcommand and calls that plugin's methods.
+For example, writing a program that injects an `Init` plugin into a `CLI` then calling `CLI.Run()` will call the
+plugin's [SubcommandMetadata][plugin-sub-command], [UpdatesMetadata][plugin-update-meta] and `Run` methods with information a user has passed to the
+program in `kubebuilder init`. Following an example:
+
+```go
+package cli
+
+import (
+ log "log/slog"
+ "github.com/spf13/cobra"
+
+ "sigs.k8s.io/kubebuilder/v4/pkg/cli"
+ cfgv3 "sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
+ "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"
+ deployimagev1alpha1 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1"
+ golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
+
+)
+
+var (
+ // The following is an example of the commands
+ // that you might have in your own binary
+ commands = []*cobra.Command{
+ myExampleCommand.NewCmd(),
+ }
+ alphaCommands = []*cobra.Command{
+ myExampleAlphaCommand.NewCmd(),
+ }
+)
+
+// GetPluginsCLI returns the plugins based CLI configured to be used in your CLI binary
+func GetPluginsCLI() (*cli.CLI) {
+ // Bundle plugin which built the golang projects scaffold by Kubebuilder go/v4
+ gov3Bundle, _ := plugin.NewBundleWithOptions(plugin.WithName(golang.DefaultNameQualifier),
+ plugin.WithVersion(plugin.Version{Number: 3}),
+ plugin.WithPlugins(kustomizecommonv2.Plugin{}, golangv4.Plugin{}),
+ )
+
+
+ c, err := cli.New(
+ // Add the name of your CLI binary
+ cli.WithCommandName("example-cli"),
+
+ // Add the version of your CLI binary
+ cli.WithVersion(versionString()),
+
+ // Register the plugins options which can be used to do the scaffolds via your CLI tool. See that we are using as example here the plugins which are implemented and provided by Kubebuilder
+ cli.WithPlugins(
+ gov3Bundle,
+ &deployimagev1alpha1.Plugin{},
+ ),
+
+ // Defines what will be the default plugin used by your binary. It means that will be the plugin used if no info be provided such as when the user runs `kubebuilder init`
+ cli.WithDefaultPlugins(cfgv3.Version, gov3Bundle),
+
+ // Define the default project configuration version which will be used by the CLI when none is informed by --project-version flag.
+ cli.WithDefaultProjectVersion(cfgv3.Version),
+
+ // Adds your own commands to the CLI
+ cli.WithExtraCommands(commands...),
+
+ // Add your own alpha commands to the CLI
+ cli.WithExtraAlphaCommands(alphaCommands...),
+
+ // Adds the completion option for your CLI
+ cli.WithCompletion(),
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ return c
+}
+
+// versionString returns the CLI version
+func versionString() string {
+ // return your binary project version
+}
+```
+
+This program can then be built and run in the following ways:
+
+Default behavior:
+
+```sh
+# Initialize a project with the default Init plugin, "go.example.com/v1".
+# This key is automatically written to a PROJECT config file.
+$ my-bin-builder init
+# Create an API and webhook with "go.example.com/v1" CreateAPI and
+# CreateWebhook plugin methods. This key was read from the config file.
+$ my-bin-builder create api [flags]
+$ my-bin-builder create webhook [flags]
+```
+
+Selecting a plugin using `--plugins`:
+
+```sh
+# Initialize a project with the "ansible.example.com/v1" Init plugin.
+# Like above, this key is written to a config file.
+$ my-bin-builder init --plugins ansible
+# Create an API and webhook with "ansible.example.com/v1" CreateAPI
+# and CreateWebhook plugin methods. This key was read from the config file.
+$ my-bin-builder create api [flags]
+$ my-bin-builder create webhook [flags]
+```
+
+### Inputs should be tracked in the PROJECT file
+
+The CLI is responsible for managing the [PROJECT file configuration][project-file-config],
+which represents the configuration of the projects scaffolded by the
+CLI tool.
+
+When extending Kubebuilder, it is recommended to ensure that your tool
+or [External Plugin][external-plugin] properly uses the
+[PROJECT file][project-file-config] to track relevant information.
+This ensures that other external tools and plugins can properly
+integrate with the project. It also allows tools features to help users
+re-scaffold their projects such as using the [Alpha Commands](./../../reference/alpha_commands.md)
+to upgrade the project scaffold to a newer version of Kubebuilder, ensuring the tracked information in the
+PROJECT file can be leveraged for various purposes.
+
+For example, plugins can check whether they support the project setup
+and re-execute commands based on the tracked inputs.
+
+#### Example
+
+By running the following command to use the
+[Deploy Image][deploy-image] plugin to scaffold
+an API and its controller:
+
+```sh
+kubebyilder create api --group example.com --version v1alpha1 --kind Memcached --image=memcached:memcached:1.6.26-alpine3.19 --image-container-command="memcached,--memory-limit=64,-o,modern,-v" --image-container-port="11211" --run-as-user="1001" --plugins="deploy-image/v1-alpha" --make=false
+```
+
+The following entry would be added to the PROJECT file:
+
+```yaml
+...
+plugins:
+ deploy-image.go.kubebuilder.io/v1-alpha:
+ resources:
+ - domain: testproject.org
+ group: example.com
+ kind: Memcached
+ options:
+ containerCommand: memcached,--memory-limit=64,-o,modern,-v
+ containerPort: "11211"
+ image: memcached:memcached:1.6.26-alpine3.19
+ runAsUser: "1001"
+ version: v1alpha1
+ - domain: testproject.org
+ group: example.com
+ kind: Busybox
+ options:
+ image: busybox:1.36.1
+ version: v1alpha1
+...
+```
+
+By inspecting the PROJECT file, it becomes possible to understand how
+the plugin was used and what inputs were provided. This not only allows
+re-execution of the command based on the tracked data but also enables
+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-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]: https://github.com/kubernetes-sigs/kubebuilder/blob/book-v4/pkg/plugin/util/util.go
+[project-file-config]: ./../../reference/project-config.md
+[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
diff --git a/docs/book/src/plugins/extending/external-plugins.md b/docs/book/src/plugins/extending/external-plugins.md
new file mode 100644
index 00000000000..bfbdfc74f76
--- /dev/null
+++ b/docs/book/src/plugins/extending/external-plugins.md
@@ -0,0 +1,181 @@
+# Creating External Plugins for Kubebuilder
+
+## Overview
+
+Kubebuilder's functionality can be extended through external plugins.
+These plugins are executables (written in any language) that follow an
+execution pattern recognized by Kubebuilder. Kubebuilder interacts with
+these plugins via `stdin` and `stdout`, enabling seamless communication.
+
+## Why Use External Plugins?
+
+External plugins enable third-party solution maintainers to integrate their tools with Kubebuilder.
+Much like Kubebuilder's own plugins, these can be opt-in, offering users
+flexibility in tool selection. By developing plugins in their repositories,
+maintainers ensure updates are aligned with their CI pipelines and can
+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
+are always willing to provide support in extending its capabilities.
+
+## How to Write an External Plugin
+
+Communication between Kubebuilder and an external plugin occurs via
+standard I/O. Any language can be used to create the plugin, as long
+as it follows the [PluginRequest][code-plugin-external] and [PluginResponse][code-plugin-external]
+structures.
+
+`PluginRequest` contains the data collected from the CLI and any previously executed plugins. Kubebuilder sends this data as a JSON object to the external plugin via `stdin`.
+
+**Fields:**
+- `apiVersion`: Version of the PluginRequest schema.
+- `args`: Command-line arguments passed to the plugin.
+- `command`: The subcommand being executed (e.g., `init`, `create api`, `create webhook`, `edit`).
+- `universe`: Map of file paths to contents, updated across the plugin chain.
+- `pluginChain` (optional): Array of plugin keys in the order they were executed. External plugins can inspect this to tailor behavior based on other plugins that ran (for example, `go.kubebuilder.io/v4` or `kustomize.common.kubebuilder.io/v2`).
+- `config` (optional): Serialized PROJECT file configuration for the current project. Use it to inspect metadata, existing resources, or plugin-specific settings. Kubebuilder omits this field before the PROJECT file exists—typically during the first `init`—so plugins should check for its presence.
+
+
+**Note:** Whenever Kubebuilder has a PROJECT file available (for example during `create api`, `create webhook`, `edit`, or a subsequent `init` run), `PluginRequest` includes the `config` field. During the very first `init` run the field is omitted because the PROJECT file does not exist yet.
+
+**Example `PluginRequest` (triggered by `kubebuilder init --plugins go/v4,sampleexternalplugin/v1 --domain my.domain`):**
+
+```json
+{
+ "apiVersion": "v1alpha1",
+ "args": ["--domain", "my.domain"],
+ "command": "init",
+ "universe": {},
+ "pluginChain": ["go.kubebuilder.io/v4", "kustomize.common.kubebuilder.io/v2", "sampleexternalplugin/v1"]
+}
+```
+
+**Example `PluginRequest` for `create api` (includes `config`):**
+```json
+{
+ "apiVersion": "v1alpha1",
+ "args": ["--group", "crew", "--version", "v1", "--kind", "Captain"],
+ "command": "create api",
+ "universe": {},
+ "pluginChain": ["go.kubebuilder.io/v4", "kustomize.common.kubebuilder.io/v2", "sampleexternalplugin/v1"],
+ "config": {
+ "domain": "my.domain",
+ "repo": "github.com/example/my-project",
+ "projectName": "my-project",
+ "version": "3",
+ "layout": ["go.kubebuilder.io/v4"],
+ "multigroup": false,
+ "resources": []
+ }
+}
+```
+
+### PluginResponse
+
+`PluginResponse` contains the modifications made by the plugin to the project. This data is serialized as JSON and returned to Kubebuilder through `stdout`.
+
+**Example `PluginResponse`:**
+```json
+{
+ "apiVersion": "v1alpha1",
+ "command": "init",
+ "metadata": {
+ "description": "The `init` subcommand initializes a project via Kubebuilder. It scaffolds a single file: `initFile`.",
+ "examples": "kubebuilder init --plugins sampleexternalplugin/v1 --domain my.domain"
+ },
+ "universe": {
+ "initFile": "A file created with the `init` subcommand."
+ },
+ "error": false,
+ "errorMsgs": []
+}
+```
+
+
+
+
+Avoid printing directly to `stdout` in your external plugin.
+Since communication between Kubebuilder and the plugin occurs through
+`stdin` and `stdout` using structured JSON, any unexpected output
+(like debug logs) may cause errors. Write logs to a file if needed.
+
+
+
+## How to Use an External Plugin
+
+### Prerequisites
+
+- Kubebuilder CLI version > 3.11.0
+- An executable for the external plugin
+- Plugin path configuration using `${EXTERNAL_PLUGINS_PATH}` or default OS-based paths:
+ - Linux: `$HOME/.config/kubebuilder/plugins/${name}/${version}/${name}`
+ - macOS: `~/Library/Application Support/kubebuilder/plugins/${name}/${version}/${name}`
+
+**Example:** For a plugin `foo.acme.io` version `v2` on Linux, the path would be `$HOME/.config/kubebuilder/plugins/foo.acme.io/v2/foo.acme.io`.
+
+### Available Subcommands
+
+External plugins can support the following Kubebuilder subcommands:
+- `init`: Project initialization
+- `create api`: Scaffold Kubernetes API definitions
+- `create webhook`: Scaffold Kubernetes webhooks
+- `edit`: Update project configuration
+
+**Optional subcommands for enhanced user experience:**
+- `metadata`: Provide plugin descriptions and examples with the `--help` flag.
+- `flags`: Inform Kubebuilder of supported flags, enabling early error detection.
+
+
+More about `flags` subcommand
+
+The `flags` subcommand in an external plugin allows for early error detection by informing Kubebuilder about the flags the plugin supports. If an unsupported flag is identified, Kubebuilder can issue an error before the plugin is called to execute.
+If a plugin does not implement the `flags` subcommand, Kubebuilder will pass all flags to the plugin, making it the external plugin's responsibility to handle any invalid flags.
+
+
+
+### Configuring Plugin Path
+
+Set the environment variable `$EXTERNAL_PLUGINS_PATH`
+to specify a custom plugin binary path:
+
+```sh
+export EXTERNAL_PLUGINS_PATH=
+```
+
+Otherwise, Kubebuilder would search for the plugins in a default path based on your OS.
+
+### Example CLI Commands
+
+You can now use it by calling the CLI commands:
+
+```sh
+# Initialize a new project with the external plugin named `sampleplugin`
+kubebuilder init --plugins sampleplugin/v1
+
+# Display help information of the `init` subcommand of the external plugin
+kubebuilder init --plugins sampleplugin/v1 --help
+
+# Create a new API with the above external plugin with a customized flag `number`
+kubebuilder create api --plugins sampleplugin/v1 --number 2
+
+# Create a webhook with the above external plugin with a customized flag `hooked`
+kubebuilder create webhook --plugins sampleplugin/v1 --hooked
+
+# Update the project configuration with the above external plugin
+kubebuilder edit --plugins sampleplugin/v1
+
+# Create new APIs with external plugins v1 and v2 by respecting the plugin chaining order
+kubebuilder create api --plugins sampleplugin/v1,sampleplugin/v2
+
+# Create new APIs with the go/v4 plugin and then pass those files to the external plugin by respecting the plugin chaining order
+kubebuilder create api --plugins go/v4,sampleplugin/v1
+```
+
+## Further resources
+
+- A [sample external plugin written in Go](https://github.com/kubernetes-sigs/kubebuilder/tree/master/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/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]: 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
new file mode 100644
index 00000000000..6482bcdc5ed
--- /dev/null
+++ b/docs/book/src/plugins/extending/testing-plugins.md
@@ -0,0 +1,108 @@
+# Write E2E Tests
+
+You can check the [Kubebuilder/v4/test/e2e/utils][utils-kb] package, which offers `TestContext` with rich methods:
+
+- [NewTestContext][new-context] helps define:
+ - A temporary folder for testing projects.
+ - A temporary controller-manager image.
+ - The [Kubectl execution method][kubectl-ktc].
+ - The CLI executable (whether `kubebuilder`, `operator-sdk`, or your extended CLI).
+
+Once defined, you can use `TestContext` to:
+
+1. **Setup the testing environment**, e.g.:
+ - Clean up the environment and create a temporary directory. See [Prepare][prepare-method].
+ - Install prerequisite CRDs. See [InstallCertManager][cert-manager-install], [InstallPrometheusManager][prometheus-manager-install].
+
+2. **Validate the plugin behavior**, e.g.:
+ - Trigger the plugin's bound subcommands. See [Init][init-subcommand], [CreateAPI][create-api-subcommand].
+ - Use [PluginUtil][plugin-util] to verify scaffolded outputs. See [InsertCode][insert-code], [ReplaceInFile][replace-in-file], [UncommentCode][uncomment-code].
+
+3. **Ensure the scaffolded output works**, e.g.:
+ - Execute commands in your `Makefile`. See [Make][make-command].
+ - Temporarily load an image of the testing controller. See [LoadImageToKindCluster][load-image-to-kind].
+ - Call Kubectl to validate running resources. See [Kubectl][kubectl-ktc].
+
+4. **Cleanup temporary resources after testing**:
+ - Uninstall prerequisite CRDs. See [UninstallPrometheusOperManager][uninstall-prometheus-manager].
+ - Delete the temporary directory. See [Destroy][destroy-method].
+
+**References**:
+- [operator-sdk e2e tests][sdk-e2e-tests]
+- [kubebuilder e2e tests][kb-e2e-tests]
+
+
+## Generate Test Samples
+
+It's straightforward to view the content of sample projects generated
+by your plugin.
+
+For example, Kubebuilder generates [sample projects][kb-samples] based
+on different plugins to validate the layouts.
+
+You can also use `TestContext` to generate folders of scaffolded
+projects from your plugin. The commands are similar to those
+mentioned in [Extending CLI Features and Plugins][extending-cli].
+
+Here’s a general workflow to create a sample project using the `go/v4` plugin (`kbc` is an instance of `TestContext`):
+
+- **To initialize a project**:
+ ```go
+ By("initializing a project")
+ err = kbc.Init(
+ "--plugins", "go/v4",
+ "--project-version", "3",
+ "--domain", kbc.Domain,
+ "--fetch-deps=false",
+ )
+ Expect(err).NotTo(HaveOccurred(), "Failed to initialize a project")
+ ```
+
+- **To define API:**
+ ```go
+ By("creating API definition")
+ err = kbc.CreateAPI(
+ "--group", kbc.Group,
+ "--version", kbc.Version,
+ "--kind", kbc.Kind,
+ "--namespaced",
+ "--resource",
+ "--controller",
+ "--make=false",
+ )
+ Expect(err).NotTo(HaveOccurred(), "Failed to create an API")
+ ```
+
+- **To scaffold webhook configurations:**
+ ```go
+ By("scaffolding mutating and validating webhooks")
+ err = kbc.CreateWebhook(
+ "--group", kbc.Group,
+ "--version", kbc.Version,
+ "--kind", kbc.Kind,
+ "--defaulting",
+ "--programmatic-validation",
+ )
+ Expect(err).NotTo(HaveOccurred(), "Failed to create an webhook")
+ ```
+
+[cert-manager-install]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.InstallCertManager
+[create-api-subcommand]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.CreateAPI
+[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
+[uninstall-prometheus-manager]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/test/e2e/utils#TestContext.UninstallPrometheusOperManager
+[utils-kb]: https://github.com/kubernetes-sigs/kubebuilder/tree/book-v4/test/e2e/utils
diff --git a/docs/book/src/plugins/kustomize-v2.md b/docs/book/src/plugins/kustomize-v2.md
new file mode 100644
index 00000000000..03d0e6f3d56
--- /dev/null
+++ b/docs/book/src/plugins/kustomize-v2.md
@@ -0,0 +1,116 @@
+# [Default Scaffold] Kustomize v2
+
+The kustomize plugin allows you to scaffold all kustomize manifests used to work with the language base plugin `base.go.kubebuilder.io/v4`.
+This plugin is used to generate the manifest under `config/` directory for the projects build within the go/v4 plugin (default scaffold).
+
+Note that projects such as [Operator-sdk][sdk] consume the Kubebuilder project as a lib and provide options to work with other languages
+like Ansible and Helm. The kustomize plugin allows them to easily keep a maintained configuration and ensure that all languages have
+the same configuration. It is also helpful if you are looking to provide nice plugins which will perform changes on top of
+what is scaffolded by default. With this approach we do not need to keep manually updating this configuration in all possible language plugins
+which uses the same and we are also
+able to create "helper" plugins which can work with many projects and languages.
+
+
+Examples
+
+You can check the kustomize content by looking at the `config/` directory provide on the sample `project-v4-*` under the [testdata][testdata]
+directory of the Kubebuilder project.
+
+
+
+## When to use it
+
+- If you are looking to scaffold the kustomize configuration manifests for your own language plugin
+- If you are looking for support on Apple Silicon (`darwin/arm64`). (_Before kustomize `4.x` the binary for this plataform is not provided_)
+- If you are looking for to begin to try out the new syntax and features provide by kustomize v4 [(More info)][release-notes-v4] and v5 [(More info)][release-notes-v5]
+- If you are NOT looking to build projects which will be used on Kubernetes cluster versions < `1.22` (_The new features provides by kustomize v4 are not officially supported and might not work with kubectl < `1.22`_)
+- If you are NOT looking to rely on special URLs in resource fields
+- If you want to use [replacements][kustomize-replacements] since [vars][kustomize-vars] are deprecated and might be removed soon
+
+## How to use it
+
+If you are looking to define that your language plugin should use kustomize use the [Bundle Plugin][bundle]
+to specify that your language plugin is a composition with your plugin responsible for scaffold
+all that is language specific and kustomize for its configuration, see:
+
+```go
+import (
+...
+ kustomizecommonv2 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/common/kustomize/v2"
+ golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
+...
+)
+
+ // Bundle plugin which built the golang projects scaffold by Kubebuilder go/v4
+ // The follow code is creating a new plugin with its name and version via composition
+ // You can define that one plugin is composite by 1 or Many others plugins
+ gov3Bundle, _ := plugin.NewBundle(plugin.WithName(golang.DefaultNameQualifier),
+ plugin.WithVersion(plugin.Version{Number: 3}),
+ plugin.WithPlugins(kustomizecommonv2.Plugin{}, golangv4.Plugin{}), // scaffold the config/ directory and all kustomize files
+ // Scaffold the Golang files and all that specific for the language e.g. go.mod, apis, controllers
+ )
+```
+
+Also, with Kubebuilder, you can use kustomize/v2 alone via:
+
+```sh
+kubebuilder init --plugins=kustomize/v2
+$ ls -la
+total 24
+drwxr-xr-x 6 camilamacedo86 staff 192 31 Mar 09:56 .
+drwxr-xr-x 11 camilamacedo86 staff 352 29 Mar 21:23 ..
+-rw------- 1 camilamacedo86 staff 129 26 Mar 12:01 .dockerignore
+-rw------- 1 camilamacedo86 staff 367 26 Mar 12:01 .gitignore
+-rw------- 1 camilamacedo86 staff 94 31 Mar 09:56 PROJECT
+drwx------ 6 camilamacedo86 staff 192 31 Mar 09:56 config
+```
+
+Or combined with the base language plugins:
+
+```sh
+# Provides the same scaffold of go/v4 plugin which is composition but with kustomize/v2
+kubebuilder init --plugins=kustomize/v2,base.go.kubebuilder.io/v4 --domain example.org --repo example.org/guestbook-operator
+```
+
+## Subcommands
+
+The kustomize plugin implements the following subcommands:
+
+* init (`$ kubebuilder init [OPTIONS]`)
+* create api (`$ kubebuilder create api [OPTIONS]`)
+* create webhook (`$ kubebuilder create api [OPTIONS]`)
+
+
+Create API and Webhook
+
+Its implementation for the subcommand create api will scaffold the kustomize manifests
+which are specific for each API, see [here][kustomize-create-api]. The same applies
+to its implementation for create webhook.
+
+
+
+## Affected files
+
+The following scaffolds will be created or updated by this plugin:
+
+* `config/*`
+
+## Further resources
+
+* Check the kustomize [plugin implementation](https://github.com/kubernetes-sigs/kubebuilder/tree/master/pkg/plugins/common/kustomize)
+* Check the [kustomize documentation][kustomize-docs]
+* Check the [kustomize repository][kustomize-github]
+* Check the [release notes][release-notes-v5] for Kustomize v5.0.0
+* Check the [release notes][release-notes-v4] for Kustomuze v4.0.0
+* Also, you can compare the `config/` directory between the samples `project-v3` and `project-v4` to check the difference in the syntax of the manifests provided by default
+
+[sdk]:https://github.com/operator-framework/operator-sdk
+[testdata]: https://github.com/kubernetes-sigs/kubebuilder/tree/master/testdata/
+[bundle]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugin/bundle.go
+[kustomize-create-api]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/common/kustomize/v2/scaffolds/api.go#L72-L84
+[kustomize-docs]: https://kustomize.io/
+[kustomize-github]: https://github.com/kubernetes-sigs/kustomize
+[kustomize-replacements]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/replacements/
+[kustomize-vars]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/vars/
+[release-notes-v5]: https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv5.0.0
+[release-notes-v4]: https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv4.0.0
diff --git a/docs/book/src/plugins/plugins-versioning.md b/docs/book/src/plugins/plugins-versioning.md
new file mode 100644
index 00000000000..a0a34dada38
--- /dev/null
+++ b/docs/book/src/plugins/plugins-versioning.md
@@ -0,0 +1,52 @@
+# Plugins Versioning
+
+| Name | Example | Description |
+|----------|-----------------------------------------|--------|
+| Kubebuilder version | `v2.2.0`, `v2.3.0`, `v2.3.1`, `v4.2.0` | Tagged versions of the Kubebuilder project, representing changes to the source code in this repository. See the [releases][kb-releases] page for binary releases. |
+| Project version | `"1"`, `"2"`, `"3"` | Project version defines the scheme of a `PROJECT` configuration file. This version is defined in a `PROJECT` file's `version`. |
+| Plugin version | `v2`, `v3`, `v4` | Represents the version of an individual plugin, as well as the corresponding scaffolding that it generates. This version is defined in a plugin key, ex. `go.kubebuilder.io/v2`. See the [design doc][cli-plugins-versioning] for more details. |
+
+### Incrementing versions
+
+For more information on how Kubebuilder release versions work, see the [semver][semver] documentation.
+
+Project versions should only be increased if a breaking change is introduced in the PROJECT file scheme itself. Changes to the Go scaffolding or the Kubebuilder CLI *do not* affect project version.
+
+Similarly, the introduction of a new plugin version might only lead to a new minor version release of Kubebuilder, since no breaking change is being made to the CLI itself. It'd only be a breaking change to Kubebuilder if we remove support for an older plugin version. See the plugins design doc [versioning section][cli-plugins-versioning]
+for more details on plugin versioning.
+
+## Introducing changes to plugins
+
+Changes made to plugins only require a plugin version increase if and only if a change is made to a plugin
+that breaks projects scaffolded with the previous plugin version. Once a plugin version `vX` is stabilized (it doesn't
+have an "alpha" or "beta" suffix), a new plugin package should be created containing a new plugin with version
+`v(X+1)-alpha`. Typically this is done by (semantically) `cp -r pkg/plugins/golang/vX pkg/plugins/golang/v(X+1)` then updating
+version numbers and paths. All further breaking changes to the plugin should be made in this package; the `vX`
+plugin would then be frozen to breaking changes.
+
+You must also add a migration guide to the [migrations][migrations]
+section of the Kubebuilder book in your PR. It should detail the steps required
+for users to upgrade their projects from `vX` to `v(X+1)-alpha`.
+
+
+
+Example
+
+Kubebuilder scaffolds projects with plugin `go.kubebuilder.io/v4` by default.
+
+You create a feature that adds a new marker to the file `main.go` scaffolded by `init` that `create api` will use to update that file.
+The changes introduced in your feature would cause errors if used with projects built with
+plugins `go.kubebuilder.io/v4` without users manually updating their projects.
+
+Thus, your changes introduce a breaking change to plugin `go.kubebuilder.io`,
+and can only be merged into plugin version `v5-alpha`.
+This plugin's package should exist already.
+
+
+
+
+[semver]: https://semver.org/
+[migrations]: ../migrations.md
+[kb-releases]:https://github.com/kubernetes-sigs/kubebuilder/releases
+[design-doc]: ./extending
+[cli-plugins-versioning]:./extending#plugin-versioning
\ No newline at end of file
diff --git a/docs/book/src/plugins/plugins.md b/docs/book/src/plugins/plugins.md
index a8d39eb028f..66dae4859b6 100644
--- a/docs/book/src/plugins/plugins.md
+++ b/docs/book/src/plugins/plugins.md
@@ -1,31 +1,47 @@
# Plugins
-Since the `3.0.0` KubeBuilder version, preliminary support for plugins was added. You can [Extend the CLI and Scaffolds][extending-cli] as well. See that when users run the CLI commands to perform the scaffolds, the plugins are used:
+Kubebuilder's architecture is fundamentally plugin-based.
+This design enables the Kubebuilder CLI to evolve while maintaining
+backward compatibility with older versions, allowing users to opt-in or
+opt-out of specific features, and enabling seamless integration
+with external tools.
-- To initialize a project with a chain of global plugins:
+By leveraging plugins, projects can extend Kubebuilder and use it as a
+library to support new functionalities or implement custom scaffolding
+tailored to their users' needs. This flexibility allows maintainers
+to build on top of Kubebuilder’s foundation, adapting it to specific
+use cases while benefiting from its powerful scaffolding engine.
-```sh
-kubebuilder init --plugins=pluginA,pluginB
-```
+Plugins offer several key advantages:
+
+- **Backward compatibility**: Ensures older layouts and project structures remain functional with newer versions.
+- **Customization**: Allows users to opt-in or opt-out for specific features (i.e. [Grafana][grafana-plugin] and [Deploy Image][deploy-image] plugins)
+- **Extensibility**: Facilitates integration with third-party tools and projects that wish to provide their own [External Plugins][external-plugins], which can be used alongside Kubebuilder to modify and enhance project scaffolding or introduce new features.
-- To perform an optional scaffold using custom plugins:
+**For example, to initialize a project with multiple global plugins:**
```sh
-kubebuilder create api --plugins=pluginA,pluginB
+kubebuilder init --plugins=pluginA,pluginB,pluginC
```
-This section details how to extend Kubebuilder and create your plugins following the same layout structures.
+**For example, to apply custom scaffolding using specific plugins:**
-
-Note
-
-For further information check the design proposal docs [Extensible CLI and Scaffolding Plugins: phase 1][plugins-phase1-design-doc] and [Extensible CLI and Scaffolding Plugins: phase 1.5][plugins-phase1-design-doc-1.5].
+```sh
+kubebuilder create api --plugins=pluginA,pluginB,pluginC
+OR
+kubebuilder create webhook --plugins=pluginA,pluginB,pluginC
+OR
+kubebuilder edit --plugins=pluginA,pluginB,pluginC
+```
-
+This section details the available plugins, how to extend Kubebuilder,
+and how to create your own plugins while following the same layout structures.
- - [Extending the CLI and Scaffolds](extending-cli.md)
- - [Creating your own plugins](creating-plugins.md)
+- [Available Plugins](./available-plugins.md)
+- [Extending](./extending.md)
+- [Plugins Versioning](./plugins-versioning.md)
-[plugins-phase1-design-doc]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1.md
-[plugins-phase1-design-doc-1.5]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/extensible-cli-and-scaffolding-plugins-phase-1-5.md
-[extending-cli]: extending-cli.md
\ No newline at end of file
+[extending-cli]: extending.md
+[grafana-plugin]: ./available/grafana-v1-alpha.md
+[deploy-image]: ./available/deploy-image-plugin-v1-alpha.md
+[external-plugins]: ./extending/external-plugins.md
\ No newline at end of file
diff --git a/docs/book/src/plugins/to-add-optional-features.md b/docs/book/src/plugins/to-add-optional-features.md
new file mode 100644
index 00000000000..d6d53a06ccf
--- /dev/null
+++ b/docs/book/src/plugins/to-add-optional-features.md
@@ -0,0 +1,17 @@
+## To add optional features
+
+The following plugins are useful to generate code and take advantage of optional features
+
+| Plugin | Key | Description |
+|-----------------------------------------------------|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [autoupdate.kubebuilder.io/v1-alpha][autoupdate] | `autoupdate/v1-alpha` | Optional helper which scaffolds a scheduled worker that helps keep your project updated with changes in the ecosystem, significantly reducing the burden of manual maintenance. |
+| [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). |
+| [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. |
+| [helm.kubebuilder.io/v1-alpha][helm-v1alpha] (deprecated) | `helm/v1-alpha` | **Deprecated** - Optional helper plugin which can be used to scaffold a Helm Chart to distribute the project under the `dist` directory. Use v2-alpha instead. |
+| [helm.kubebuilder.io/v2-alpha][helm-v2alpha] | `helm/v2-alpha` | Optional helper plugin which dynamically generates Helm charts from kustomize output, preserving all customizations |
+
+[grafana]: ./available/grafana-v1-alpha.md
+[deploy]: ./available/deploy-image-plugin-v1-alpha.md
+[helm-v1alpha]: ./available/helm-v1-alpha.md
+[helm-v2alpha]: ./available/helm-v2-alpha.md
+[autoupdate]: ./available/autoupdate-v1-alpha.md
\ No newline at end of file
diff --git a/docs/book/src/plugins/to-be-extended.md b/docs/book/src/plugins/to-be-extended.md
new file mode 100644
index 00000000000..962eda6f44d
--- /dev/null
+++ b/docs/book/src/plugins/to-be-extended.md
@@ -0,0 +1,24 @@
+## To be extended
+
+The following plugins are useful for other tools and [External Plugins][external-plugins] which are looking to extend the
+Kubebuilder functionality.
+
+You can use the kustomize plugin, which is responsible for scaffolding the
+kustomize files under `config/`. The base language plugins are responsible
+for scaffolding the necessary Golang files, allowing you to create your
+own plugins for other languages (e.g., [Operator-SDK][sdk] enables
+users to work with Ansible/Helm) or add additional functionality.
+
+For example, [Operator-SDK][sdk] has a plugin which integrates the
+projects with [OLM][olm] by adding its own features on top.
+
+| Plugin | Key | Description |
+|--------------------------------------------------------|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
+| [kustomize.common.kubebuilder.io/v2][kustomize-plugin] | `kustomize/v2` | Responsible for scaffolding all [kustomize][kustomize] files under the `config/` directory |
+| `base.go.kubebuilder.io/v4` | `base/v4` | Responsible for scaffolding all files which specifically requires Golang. This plugin is used in the composition to create the plugin (`go/v4`) |
+
+[kustomize]: https://kustomize.io/
+[sdk]: https://github.com/operator-framework/operator-sdk
+[olm]: https://olm.operatorframework.io/
+[kustomize-plugin]: ./available/kustomize-v2.md
+[external-plugins]: ./extending/external-plugins.md
diff --git a/docs/book/src/plugins/to-scaffold-project.md b/docs/book/src/plugins/to-scaffold-project.md
new file mode 100644
index 00000000000..7e1744e94fc
--- /dev/null
+++ b/docs/book/src/plugins/to-scaffold-project.md
@@ -0,0 +1,10 @@
+## To scaffold the projects
+
+The following plugins are useful to scaffold the whole project with the tool.
+
+| Plugin | Key | Description |
+|--------------------------------------------------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [go.kubebuilder.io/v4 - (Default scaffold with Kubebuilder init)][go-v4] | `go/v4` | Scaffold composite by `base.go.kubebuilder.io/v4` and [kustomize.common.kubebuilder.io/v2][kustomize-v2]. Responsible for scaffolding Golang projects and its configurations. |
+
+[go-v4]: ./available/go-v4-plugin.md
+[kustomize-v2]: ./available/kustomize-v2.md
\ No newline at end of file
diff --git a/docs/book/src/quick-start.md b/docs/book/src/quick-start.md
index 0da69c0b503..08fa5a1b213 100644
--- a/docs/book/src/quick-start.md
+++ b/docs/book/src/quick-start.md
@@ -9,19 +9,15 @@ This Quick Start guide will cover:
## Prerequisites
-- [go](https://golang.org/dl/) version v1.15+ and < 1.16.
+- [go](https://go.dev/dl/) version v1.24.6+
- [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.
-Versions and Supportability
+Versions Compatibility and Supportability
-Projects created by Kubebuilder contain a Makefile that will install tools at versions defined at creation time. Those tools are:
-- [kustomize](https://kubernetes-sigs.github.io/kustomize/)
-- [controller-gen](https://github.com/kubernetes-sigs/controller-tools)
-
-The versions which are defined in the `Makefile` and `go.mod` files are the versions tested and therefore is recommend to use the specified versions.
+Please, ensure that you see the [guidance](./versions_compatibility_supportability.md).
@@ -31,21 +27,22 @@ Install [kubebuilder](https://sigs.k8s.io/kubebuilder):
```bash
# download kubebuilder and install locally.
-curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
-chmod +x kubebuilder && mv kubebuilder /usr/local/bin/
+curl -L -o kubebuilder "https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)"
+chmod +x kubebuilder && sudo mv kubebuilder /usr/local/bin/
```
-Using master branch
+Using the Master Branch
-You can work with a master snapshot by installing from `https://go.kubebuilder.io/dl/master/$(go env GOOS)/$(go env GOARCH)`.
+You can work with the master branch by cloning the repository and running `make install` to generate the binary.
+Please follow the steps in the section **How to Build Kubebuilder Locally** from the [Contributing Guide](https://github.com/kubernetes-sigs/kubebuilder/blob/master/CONTRIBUTING.md#how-to-build-kubebuilder-locally).
Enabling shell autocompletion
-Kubebuilder provides autocompletion support for Bash and Zsh via the command `kubebuilder completion `, which can save you a lot of typing. For further information see the [completion](./reference/completion.md) document.
+Kubebuilder provides autocompletion support via the command `kubebuilder completion `, which can save you a lot of typing. For further information see the [completion](./reference/completion.md) document.
@@ -69,7 +66,6 @@ Read the [Go modules blogpost][go-modules-blogpost] if unfamiliar with the modul
-
## Create an API
Run the following command to create a new API (group/version) as `webapp/v1` and the new Kind(CRD) `Guestbook` on it:
@@ -82,16 +78,21 @@ kubebuilder create api --group webapp --version v1 --kind Guestbook
Press Options
If you press `y` for Create Resource [y/n] and for Create Controller [y/n] then this will create the files `api/v1/guestbook_types.go` where the API is defined
-and the `controllers/guestbook_controller.go` where the reconciliation business logic is implemented for this Kind(CRD).
+and the `internal/controllers/guestbook_controller.go` where the reconciliation business logic is implemented for this Kind(CRD).
-
**OPTIONAL:** Edit the API definition and the reconciliation business
logic. For more info see [Designing an API](/cronjob-tutorial/api-design.md) and [What's in
a Controller](cronjob-tutorial/controller-overview.md).
-Click here to see an example. `(api/v1/guestbook_types.go)`
+If you are editing the API definitions, generate the manifests such as Custom Resources (CRs) or Custom Resource Definitions (CRDs) using
+
+```bash
+make manifests
+```
+
+Click here to see an example. (api/v1/guestbook_types.go)
```go
@@ -111,7 +112,7 @@ type GuestbookSpec struct {
ConfigMapName string `json:"configMapName"`
// +kubebuilder:validation:Enum=Phone;Address;Name
- Type string `json:"alias,omitempty"`
+ Type string `json:"type,omitempty"`
}
// GuestbookStatus defines the observed state of Guestbook
@@ -144,10 +145,19 @@ type Guestbook struct {
+
+ `+kubebuilder` markers
+
+`+kubebuilder` are [markers][markers] processed by [controller-gen][controller-gen]
+to generate CRDs and RBAC. Kubebuilder also provides [scaffolding markers][scaffolding-markers]
+to inject code into existing files and simplify common tasks. See `cmd/main.go` for examples.
+
+
+
## Test It Out
-You'll need a Kubernetes cluster to run against. You can use
-[KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or
+You'll need a Kubernetes cluster to run against. You can use
+[KinD][kind] to get a local cluster for testing, or
run against a remote cluster.
@@ -159,27 +169,53 @@ kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).
Install the CRDs into the cluster:
+
```bash
make install
```
-Run your controller (this will run in the foreground, so switch to a new
+For quick feedback and code-level debugging, run your controller (this will run in the foreground, so switch to a new
terminal if you want to leave it running):
+
```bash
make run
```
## Install Instances of Custom Resources
-If you pressed `y` for Create Resource [y/n] then you created an (CR)Custom Resource for your (CRD)Custom Resource Definition in your samples (make sure to edit them first if you've changed the
-API definition):
+If you pressed `y` for Create Resource [y/n] then you created a CR for your CRD in your
+`config/samples/` directory.
+
+Edit `config/samples/webapp_v1_guestbook.yaml` to contain a valid `spec`. For example:
+
+```yaml
+# ...
+spec:
+ foo: bar
+```
+
+Hint: "foo" is a string field defined in `api/v1/guestbook_types.go`:
+
+```go
+// foo is an example field of Guestbook. Edit guestbook_types.go to remove/update
+// +optional
+Foo *string `json:"foo,omitempty"`
+```
+
+```bash
+kubectl apply -k config/samples/
+```
+
+You can have a look at your applied resource now:
```bash
-kubectl apply -f config/samples/
+kubectl get guestbooks.webapp.my.domain guestbook-sample -o yaml
```
## Run It On the Cluster
+When your controller is ready to be packaged and tested in other clusters.
+
Build and push your image to the location specified by `IMG`:
```bash
@@ -193,10 +229,26 @@ make deploy IMG=/:tag
```
+Registry Permission
+
+This image ought to be published in the personal registry you specified. And it is required to have access to pull the image from the working environment.
+Make sure you have the proper permission to the registry if the above commands don't work.
+
+Consider incorporating [Kind][kind] into your workflow for a faster, more efficient local development and CI experience.
+Note that, if you're using a [Kind][kind] cluster, there's no need to push your image to a remote container registry.
+You can directly load your local image into your specified [Kind][kind] cluster:
+
+```bash
+kind load docker-image :tag --name
+```
+
+It is highly recommended to use [Kind][kind] for development purposes and CI.
+To know more, see: [Using Kind For Development Purposes and CI](./reference/kind.md)
+
RBAC errors
If you encounter RBAC errors, you may need to grant yourself cluster-admin
-privileges or be logged in as admin. See [Prerequisites for using Kubernetes RBAC on GKE cluster v1.11.x and older][pre-rbc-gke] which may be your case.
+privileges or be logged in as admin. See [Prerequisites for using Kubernetes RBAC on GKE cluster v1.11.x and older][pre-rbc-gke] which may be your case.
@@ -210,19 +262,77 @@ make uninstall
## Undeploy controller
-UnDeploy the controller to the cluster:
+Undeploy the controller to the cluster:
```bash
make undeploy
```
+## Using Plugins
+
+Kubebuilder design is based on [Plugins][plugins] and you can use
+[available plugins][available-plugins] to add optional features to your project.
+
+### Creating an API and Controller with code to manage an image
+
+For example, you can scaffold an API and controller that
+manages container images by using the [deploy-image plugin][deploy-image-v1-alpha]:
+
+```bash
+kubebuilder create api --group webapp --version v1alpha1 --kind Busybox --image=busybox:1.36.1 --plugins="deploy-image/v1-alpha"
+```
+
+This command generates:
+
+- The API definition in `api/v1alpha1/busybox_types.go`.
+- The controller logic in `internal/controllers/busybox_controller.go`.
+- A test scaffold in `internal/controllers/busybox_controller_test.go`, which uses [EnvTest][envtest] for integration-style testing.
+
+
+References and Examples
+
+Use the [Deploy Image Plugin (deploy-image/v1-alpha)][deploy-image-v1-alpha] as a reference when creating your project.
+It follows Kubernetes conventions and recommended good practices.
+
+
+
+### Keeping your project up to date with ecosystem changes
+
+Kubebuilder provides the [AutoUpdate Plugin][autoupdate-v1-alpha]
+to help keep your project aligned with the latest ecosystem changes.
+When a new release is available, the plugin opens an **Issue** with a
+Pull Request compare link. You can then review the updates and, if helpful,
+use [GitHub AI models][ai-gh-models] to understand what changes are needed to keep your project current.
+
+```bash
+kubebuilder edit --plugins="autoupdate/v1-alpha"
+```
+
+This command scaffolds a GitHub workflow file at `.github/workflows/autoupdate.yml`.
## Next Step
-Now, see the [architecture concept diagram][architecture-concept-diagram] for a better overview and follow up the [CronJob tutorial][cronjob-tutorial] to better understand how it works by developing a demo example project.
+- Proceed with the [Getting Started Guide][getting-started], which should take no more than 30 minutes and will
+ provide a solid foundation.
+- Afterward, dive into the [CronJob Tutorial][cronjob-tutorial] to deepen your
+ understanding by developing a demo project.
+- Ensure that you understand the APIs and Groups concepts [Groups and Versions and Kinds, oh my!][gkv-doc]
+before designing your own API and 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-module-blogpost]:https://blog.golang.org/using-go-modules
-[envtest]: https://book.kubebuilder.io/reference/testing/envtest.html
+[GOPATH-golang-docs]: https://go.dev/doc/code.html#GOPATH
+[go-modules-blogpost]: https://blog.go.dev/using-go-modules
[architecture-concept-diagram]: architecture.md
+[kustomize]: https://github.com/kubernetes-sigs/kustomize
+[getting-started]: getting-started.md
+[plugins]: plugins/plugins.md
+[available-plugins]: plugins/available-plugins.md
+[envtest]: ./reference/envtest.md
+[autoupdate-v1-alpha]: plugins/available/autoupdate-v1-alpha.md
+[deploy-image-v1-alpha]: plugins/available/deploy-image-plugin-v1-alpha.md
+[gkv-doc]: cronjob-tutorial/gvks.md
+[kind]: https://sigs.k8s.io/kind
+[markers]: reference/markers.md
+[controller-gen]: https://sigs.k8s.io/controller-tools/cmd/controller-gen
+[scaffolding-markers]: reference/markers/scaffold.md
+[ai-gh-models]: https://docs.github.com/en/github-models/about-github-models
diff --git a/docs/book/src/reference/admission-webhook.md b/docs/book/src/reference/admission-webhook.md
index bc32756ea58..222d49f7437 100644
--- a/docs/book/src/reference/admission-webhook.md
+++ b/docs/book/src/reference/admission-webhook.md
@@ -1,6 +1,6 @@
# Admission Webhooks
-Admission webhooks are HTTP callbacks that receive admission requests, process
+[Admission webhooks](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#what-are-admission-webhooks) are HTTP callbacks that receive admission requests, process
them and return admission responses.
Kubernetes provides the following types of admission webhooks:
@@ -21,3 +21,112 @@ if you want to authenticate the clients, you can configure the apiserver to use
basic auth, bearer token, or a cert to authenticate itself to the webhooks.
You can find detailed steps
[here](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#authenticate-apiservers).
+
+
+Execution Order
+
+**Validating webhooks run after all mutating webhooks**, so you don't need to worry about another webhook changing an
+object after your validation has accepted it.
+
+
+
+## Custom Webhook Paths
+
+By default, Kubebuilder generates webhook paths based on the resource's group, version, and kind. For example:
+- Mutating webhook for `batch/v1/CronJob`: `/mutate-batch-v1-cronjob`
+- Validating webhook for `batch/v1/CronJob`: `/validate-batch-v1-cronjob`
+
+You can specify custom paths for webhooks using dedicated flags:
+
+```bash
+# Custom path for defaulting webhook
+kubebuilder create webhook --group batch --version v1 --kind CronJob \
+ --defaulting --defaulting-path=/my-custom-mutate-path
+
+# Custom path for validation webhook
+kubebuilder create webhook --group batch --version v1 --kind CronJob \
+ --programmatic-validation --validation-path=/my-custom-validate-path
+
+# Both webhooks with different custom paths
+kubebuilder create webhook --group batch --version v1 --kind CronJob \
+ --defaulting --programmatic-validation \
+ --defaulting-path=/custom-mutate --validation-path=/custom-validate
+```
+
+
+Version Requirements
+
+Custom webhook paths require **controller-runtime v0.21+**. In earlier versions (< `v0.21`), the webhook path follows a
+fixed pattern based on the resource's group, version, and kind, and cannot be customized.
+
+
+
+## Handling Resource Status in Admission Webhooks
+
+
+Modify status
+
+**You cannot modify or default the status of a resource using a mutating admission webhook**.
+Set initial status in your controller when you first see a new object.
+
+
+
+### Understanding Why:
+
+#### Mutating Admission Webhooks
+
+Mutating Admission Webhooks are primarily designed to intercept and modify requests concerning the creation,
+modification, or deletion of objects. Though they possess the capability to modify an object's specification,
+directly altering its status isn't deemed a standard practice,
+often leading to unintended results.
+
+```go
+// MutatingWebhookConfiguration allows for modification of objects.
+// However, direct modification of the status might result in unexpected behavior.
+type MutatingWebhookConfiguration struct {
+ ...
+}
+```
+
+#### Setting Initial Status
+
+For those diving into custom controllers for custom resources, it's imperative to grasp the concept of setting an
+initial status. This initialization typically takes place within the controller itself. The moment the controller
+identifies a new instance of its managed resource, primarily through a watch mechanism, it holds the authority
+to assign an initial status to that resource.
+
+```go
+// Custom controller's reconcile function might look something like this:
+func (r *ReconcileMyResource) Reconcile(request reconcile.Request) (reconcile.Result, error) {
+ // ...
+ // Upon discovering a new instance, set the initial status
+ instance.Status = SomeInitialStatus
+ // ...
+}
+```
+
+#### Status Subresource
+
+Delving into Kubernetes custom resources, a clear demarcation exists between the spec (depicting the desired state)
+and the status (illustrating the observed state). Activating the /status subresource for a custom resource definition
+(CRD) bifurcates the `status` and `spec`, each assigned to its respective API endpoint.
+This separation ensures that changes introduced by users, such as modifying the spec, and system-driven updates,
+like status alterations, remain distinct. Leveraging a mutating webhook to tweak the status during a spec-modifying
+operation might not pan out as expected, courtesy of this isolation.
+
+```yaml
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ name: myresources.mygroup.mydomain
+spec:
+ ...
+ subresources:
+ status: {} # Enables the /status subresource
+```
+
+#### Conclusion
+
+While certain edge scenarios might allow a mutating webhook to seamlessly modify the status, treading this path isn't a
+universally acclaimed or recommended strategy. Entrusting the controller logic with status updates remains the
+most advocated approach.
diff --git a/docs/book/src/reference/alpha_commands.md b/docs/book/src/reference/alpha_commands.md
new file mode 100644
index 00000000000..c1beec917a9
--- /dev/null
+++ b/docs/book/src/reference/alpha_commands.md
@@ -0,0 +1,23 @@
+# Alpha Commands
+
+Kubebuilder provides experimental **alpha commands** to assist with advanced operations such as
+project migration and scaffold regeneration.
+
+These commands are designed to simplify tasks that were previously manual and error-prone
+by automating or partially automating the process.
+
+
+ Alpha commands are experimental
+
+Alpha commands are under active development and may change or be removed in future releases.
+They make local changes to your project and may delete files during execution.
+
+Always ensure your work is committed or backed up before using them.
+
+
+The following alpha commands are currently available:
+
+- [`alpha generate`](./../reference/commands/alpha_generate.md) — Re-scaffold the project using the installed CLI version
+- [`alpha update`](./../reference/commands/alpha_update.md) — Automate the migration process via 3-way merge using scaffold snapshots
+
+For more information, see each command's dedicated documentation.
diff --git a/docs/book/src/reference/artifacts.md b/docs/book/src/reference/artifacts.md
index 99d1b040a2d..03ef63a9ba9 100644
--- a/docs/book/src/reference/artifacts.md
+++ b/docs/book/src/reference/artifacts.md
@@ -1,16 +1,78 @@
# Artifacts
-Kubebuilder publishes test binaries and container images in addition
-to the main binary releases.
+To test your controllers, you will need to use the tarballs containing the required binaries:
-## Test Binaries
+```shell
+./bin/k8s/
+└── 1.25.0-darwin-amd64
+ ├── etcd
+ ├── kube-apiserver
+ └── kubectl
+```
-You can find test binary tarballs for all Kubernetes versions and host platforms at `https://go.kubebuilder.io/test-tools`.
-You can find a test binary tarball for a particular Kubernetes version and host platform at `https://go.kubebuilder.io/test-tools/${version}/${os}/${arch}`.
+These tarballs are released by [controller-tools](https://github.com/kubernetes-sigs/controller-tools),
+and you can find the list of available versions at: [envtest-releases.yaml](https://github.com/kubernetes-sigs/controller-tools/blob/main/envtest-releases.yaml).
-## Container Images
+When you run `make envtest` or `make test`, the necessary tarballs are downloaded and properly
+configured for your project.
-You can find all container image versions for a particular platform at `https://go.kubebuilder.io/images/${os}/${arch}`
-or at `gcr.io/kubebuilder/thirdparty-${os}-${arch}`.
-You can find the container image for a particular version and platform at `https://go.kubebuilder.io/images/${os}/${arch}/${version}`
-or at `gcr.io/kubebuilder/thirdparty-${os}-${arch}:${version}`.
+
+Setup ENV TEST tool
+
+To learn more about the tooling used to configure ENVTEST, which is utilized in the `setup-envtest`
+target in the Makefile of projects built with Kubebuilder, see the [README](https://github.com/kubernetes-sigs/controller-runtime/blob/main/tools/setup-envtest/README.md)
+of its tooling. Additionally, you can find more information by reviewing the Kubebuilder [ENVTEST][env-test-doc] documentation.
+
+
+
+
+
+ IMPORTANT: Action Required: Ensure that you no longer use https://storage.googleapis.com/kubebuilder-tools
+
+**Artifacts provided under [https://storage.googleapis.com/kubebuilder-tools](https://storage.googleapis.com/kubebuilder-tools) are deprecated and Kubebuilder maintainers are no longer able to support, build, or ensure the promotion of these artifacts.**
+
+You will find the [ENVTEST][env-test-doc] binaries available in the new location from k8s release `1.28`, see: [https://github.com/kubernetes-sigs/controller-tools/blob/main/envtest-releases.yaml](https://github.com/kubernetes-sigs/controller-tools/blob/main/envtest-releases.yaml).
+Also, binaries to test your controllers after k8s `1.29.3` will no longer be found in the old location.
+
+**New binaries are only promoted in the new location**.
+
+**You should ensure that your projects are using the new location.**
+Please ensure you use `setup-envtest` from the controller-runtime `release v0.19.0` to be able to download those.
+**This update is fully transparent for Kubebuilder users.**
+
+The artefacts for [ENVTEST][env-test-doc] k8s `1.31` are exclusively available at: [Controller Tools Releases][controller-gen].
+
+You can refer to the Makefile of the Kubebuilder scaffold and observe that the envtest setup is consistently aligned across all controller-runtime releases. Starting from `release-0.19`, it is configured to automatically download the artefact from the correct location, **ensuring that kubebuilder users are not impacted.**
+
+```shell
+## Tool Binaries
+..
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+...
+
+## Tool Versions
+...
+#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}')
+...
+.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)
+ $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
+```
+
+
+
+[env-test-doc]: ./envtest.md
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[controller-gen]: https://github.com/kubernetes-sigs/controller-tools/releases
diff --git a/docs/book/src/reference/commands/alpha_generate.md b/docs/book/src/reference/commands/alpha_generate.md
new file mode 100644
index 00000000000..46c79163d08
--- /dev/null
+++ b/docs/book/src/reference/commands/alpha_generate.md
@@ -0,0 +1,93 @@
+# Regenerate your project with (`alpha generate`)
+
+## Overview
+
+The `kubebuilder alpha generate` command re-scaffolds your project using the currently installed
+CLI and plugin versions.
+
+It regenerates the full scaffold based on the configuration specified in your [PROJECT][project-config] file.
+This allows you to apply the latest layout changes, plugin features, and code generation improvements introduced
+in newer Kubebuilder releases.
+
+You may choose to re-scaffold the project in-place (overwriting existing files) or in a separate
+directory for diff-based inspection and manual integration.
+
+
+ Deletes files during scaffold regeneration
+When executed in-place, this command deletes all files except `.git` and `PROJECT`.
+
+Always back up your project or use version control before running this command.
+
+
+## When to Use It?
+
+You can use `kubebuilder alpha generate` to upgrade your project scaffold when new changes are introduced
+in Kubebuilder. This includes updates to plugins (for example, `go.kubebuilder.io/v3` → `go.kubebuilder.io/v4`)
+or the CLI releases (for example, 4.3.1 → latest) .
+
+This command is helpful when you want to:
+
+- Update your project to use the latest layout or plugin version
+- Regenerate your project scaffold to include recent changes
+- Compare the current scaffold with the latest and apply updates manually
+- Create a clean scaffold for reviewing or testing changes
+
+Use this command when you want full control of the upgrade process.
+It is also useful if your project was created with an older CLI version and does not support `alpha update`.
+
+This approach allows you to compare changes between your current branch and upstream
+scaffold updates (e.g., from the main branch), and helps you overlay custom code atop the new scaffold.
+
+
+Looking for a more automated migration?
+
+If you want to upgrade your project scaffold with less manual work,
+try [`kubebuilder alpha update`](./alpha_update.md).
+
+It uses a 3-way merge to keep your code and apply the latest scaffold changes automatically.
+Use `alpha generate` if `alpha update` is not available for your project yet
+or if you prefer to handle changes manually.
+
+
+
+## How to Use It?
+
+### Upgrade your current project to CLI version installed (i.e. latest scaffold)
+
+```sh
+kubebuilder alpha generate
+```
+
+After running this command, your project will be re-scaffolded in place.
+You can then compare the local changes with your main branch to see what was updated,
+and re-apply your custom code on top as needed.
+
+### Generate Scaffold to a New Directory
+
+Use the `--input-dir` and `--output-dir` flags to specify input and output paths.
+
+```sh
+kubebuilder alpha generate \
+ --input-dir=/path/to/existing/project \
+ --output-dir=/path/to/new/project
+```
+
+After running the command, you can inspect the generated scaffold in the specified output directory.
+
+### Flags
+
+| Flag | Description |
+|------------------|-----------------------------------------------------------------------------|
+| `--input-dir` | Path to the directory containing the `PROJECT` file. Defaults to CWD. Deletes all files except `.git` and `PROJECT`. |
+| `--output-dir` | Directory where the new scaffold will be written. If unset, re-scaffolds in-place. |
+| `--plugins` | Plugin keys to use for this generation. |
+| `-h, --help` | Show help for this command. |
+
+
+## Further Resources
+
+- [Video demo on how it works](https://youtu.be/7997RIbx8kw?si=ODYMud5lLycz7osp)
+- [Design proposal documentation](../../../../../designs/helper_to_upgrade_projects_by_rescaffolding.md)
+
+[example]: ../../../../../testdata/project-v4-with-plugins/PROJECT
+[project-config]: ../../reference/project-config.md
\ No newline at end of file
diff --git a/docs/book/src/reference/commands/alpha_update.md b/docs/book/src/reference/commands/alpha_update.md
new file mode 100644
index 00000000000..fed312b1670
--- /dev/null
+++ b/docs/book/src/reference/commands/alpha_update.md
@@ -0,0 +1,263 @@
+# Update Your Project with (`alpha update`)
+
+## Overview
+
+`kubebuilder alpha update` upgrades your project’s scaffold to a newer Kubebuilder release using a **3-way Git merge**. It rebuilds clean scaffolds for the old and new versions, merges your current code into the new scaffold, and gives you a reviewable output branch.
+It takes care of the heavy lifting so you can focus on reviewing and resolving conflicts,
+not re-applying your code.
+
+By default, the final result is **squashed into a single commit** on a dedicated output branch.
+If you prefer to keep the full history (no squash), use `--show-commits`.
+
+
+ Automate this process
+
+You can reduce the burden of keeping your project up to date by using the
+[AutoUpdate Plugin][autoupdate-plugin] which
+automates the process of running `kubebuilder alpha update` on a schedule
+workflow when new Kubebuilder releases are available.
+
+Moreover, you will be able to get help from [AI models][ai-gh-models] to understand what changes are needed to keep your project up to date
+and how to solve conflicts if any are faced.
+
+
+
+## When to Use It
+
+Use this command when you:
+
+- Want to move to a newer Kubebuilder version or plugin layout
+- Want to review scaffold changes on a separate branch
+- Want to focus on resolving merge conflicts (not re-applying your custom code)
+
+## How It Works
+
+You tell the tool the **new version**, and which branch has your project.
+It rebuilds both scaffolds, merges your code into the new one with a **3-way merge**,
+and gives you an output branch you can review and merge safely.
+You decide if you want one clean commit, the full history, or an auto-push to remote.
+
+### Step 1: Detect versions
+- It looks at your `PROJECT` file or the flags you pass.
+- Decides which **old version** you are coming from by reading the `cliVersion` field in the `PROJECT` file (if available).
+- Figures out which **new version** you want (defaults to the latest release).
+- Chooses which branch has your current code (defaults to `main`).
+
+### Step 2: Create scaffolds
+The command creates three temporary branches:
+- **Ancestor**: a clean project scaffold from the **old version**.
+- **Original**: a snapshot of your **current code**.
+- **Upgrade**: a clean scaffold from the **new version**.
+
+### Step 3: Do a 3-way merge
+- Merges **Original** (your code) into **Upgrade** (the new scaffold) using Git’s **3-way merge**.
+- This keeps your customizations while pulling in upstream changes.
+- If conflicts happen:
+ - **Default** → stop and let you resolve them manually.
+ - **With `--force`** → continue and commit even with conflict markers. **(ideal for automation)**
+- Runs `make manifests generate fmt vet lint-fix` to tidy things up.
+
+### Step 4: Write the output branch
+- By default, everything is **squashed into one commit** on a safe output branch:
+ `kubebuilder-update-from--to-`.
+- You can change the behavior:
+ - `--show-commits`: keep the full history.
+ - `--restore-path`: in squash mode, restore specific files (like CI configs) from your base branch.
+ - `--output-branch`: pick a custom branch name.
+ - `--push`: push the result to `origin` automatically.
+ - `--git-config`: sets git configurations.
+ - `--open-gh-issue`: create a GitHub issue with a checklist and compare link (requires `gh`).
+ - `--use-gh-models`: add an AI overview **comment** to that issue using `gh models`
+
+### Step 5: Cleanup
+- Once the output branch is ready, all the temporary working branches are deleted.
+- You are left with one clean branch you can test, review, and merge back into your main branch.
+
+## How to Use It (commands)
+
+Run from your project root:
+
+```shell
+kubebuilder alpha update
+```
+
+Pin versions and base branch:
+
+```shell
+kubebuilder alpha update \
+--from-version v4.5.2 \
+--to-version v4.6.0 \
+--from-branch main
+```
+Automation-friendly (proceed even with conflicts):
+
+```shell
+kubebuilder alpha update --force
+```
+
+Keep full history instead of squashing:
+```
+kubebuilder alpha update --from-version v4.5.0 --to-version v4.7.0 --force --show-commits
+```
+
+Default squash but **preserve** CI/workflows from the base branch:
+
+```shell
+kubebuilder alpha update --force \
+--restore-path .github/workflows \
+--restore-path docs
+```
+
+Use a custom output branch name:
+
+```shell
+kubebuilder alpha update --force \
+--output-branch upgrade/kb-to-v4.7.0
+```
+
+Run update and push the result to origin:
+
+```shell
+kubebuilder alpha update --from-version v4.6.0 --to-version v4.7.0 --force --push
+```
+
+## Handling Conflicts (`--force` vs default)
+
+When you use `--force`, Git finishes the merge even if there are conflicts.
+The commit will include markers like:
+
+```shell
+<<<<<<< HEAD
+Your changes
+=======
+Incoming changes
+>>>>>>> (original)
+```
+
+This allows you to run the command in CI or cron jobs without manual intervention.
+
+- Without `--force`: the command stops on the merge branch and prints guidance; no commit is created.
+- With `--force`: the merge is committed (merge or output branch) and contains the markers.
+
+After you fix conflicts, always run:
+
+```shell
+make manifests generate fmt vet lint-fix
+# or
+make all
+```
+
+## Using with GitHub Issues (`--open-gh-issue`) and AI (`--use-gh-models`) assistance
+
+Pass `--open-gh-issue` to have the command create a GitHub **Issue** in your repository
+to assist with the update. Also, if you also pass `--use-gh-models`, the tool posts a follow-up comment
+on that Issue with an AI-generated overview of the most important changes plus brief conflict-resolution
+guidance.
+
+### Examples
+
+Create an Issue with a compare link:
+```shell
+kubebuilder alpha update --open-gh-issue
+```
+
+Create an Issue **and** add an AI summary:
+```shell
+kubebuilder alpha update --open-gh-issue --use-gh-models
+```
+
+### What you’ll see
+
+The command opens an Issue that links to the diff so you can create the PR and review it, for example:
+
+
+
+With `--use-gh-models`, an AI comment highlights key changes and suggests how to resolve any conflicts:
+
+
+
+Moreover, AI models are used to help you understand what changes are needed to keep your project up to date,
+and to suggest resolutions if conflicts are encountered, as in the following example:
+
+### Automation
+
+This integrates cleanly with automation. The [`autoupdate.kubebuilder.io/v1-alpha`][autoupdate-plugin] plugin can scaffold a GitHub Actions workflow that runs the command on a schedule (e.g., weekly). When a new Kubebuilder release is available, it opens an Issue with a compare link so you can create the PR and review it.
+
+## Changing Extra Git configs only during the run (does not change your ~/.gitconfig)_
+
+By default, `kubebuilder alpha update` applies safe Git configs:
+`merge.renameLimit=999999`, `diff.renameLimit=999999`, `merge.conflictStyle=merge`
+You can add more, or disable them.
+
+- **Add more on top of defaults**
+```shell
+kubebuilder alpha update \
+ --git-config rerere.enabled=true
+```
+
+- **Disable defaults entirely**
+```shell
+kubebuilder alpha update --git-config disable
+```
+
+- **Disable defaults and set your own**
+
+```shell
+kubebuilder alpha update \
+ --git-config disable \
+ --git-config rerere.enabled=true
+```
+
+
+ You might need to upgrade your project first
+
+This command uses `kubebuilder alpha generate` under the hood.
+We support projects created with v4.5.0+ .
+If yours is older, first run `kubebuilder alpha generate` once to modernize the scaffold.
+After that, you can use `kubebuilder alpha update` for future upgrades.
+
+Projects created with **Kubebuilder v4.6.0+** include `cliVersion` in the `PROJECT` file.
+We use that value to pick the correct CLI for re-scaffolding.
+
+
+
+## Flags
+
+| Flag | Description |
+|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `--force` | Continue even if merge conflicts happen. Conflicted files are committed with conflict markers (CI/cron friendly). |
+| `--from-branch` | Git branch that holds your current project code. Defaults to `main`. |
+| `--from-version` | Kubebuilder release to update **from** (e.g., `v4.6.0`). If unset, read from the `PROJECT` file when possible. |
+| `--git-config` | Repeatable. Pass per-invocation Git config as `-c key=value`. **Default** (if omitted): `-c merge.renameLimit=999999 -c diff.renameLimit=999999`. Your configs are applied on top. To disable defaults, include `--git-config disable`. |
+| `--open-gh-issue` | Create a GitHub issue with a pre-filled checklist and compare link after the update completes (requires `gh`). |
+| `--output-branch` | Name of the output branch. Default: `kubebuilder-update-from--to-`. |
+| `--push` | Push the output branch to the `origin` remote after the update completes. |
+| `--restore-path` | Repeatable. Paths to preserve from the base branch when squashing (e.g., `.github/workflows`). **Not supported** with `--show-commits`. |
+| `--show-commits` | Keep full history (do not squash). **Not compatible** with `--restore-path`. |
+| `--to-version` | Kubebuilder release to update **to** (e.g., `v4.7.0`). If unset, defaults to the latest available release. |
+| `--use-gh-models` | Post an AI overview as an issue comment using `gh models`. Requires `gh` + `gh-models` extension. Effective only when `--open-gh-issue` is also set. |
+| `-h, --help` | Show help for this command. |
+
+## Demonstration
+
+VIDEO
+
+
+About this demo
+
+This video was recorded with Kubebuilder release `v7.0.1`.
+Since then, the command has been improved,
+so the current behavior may differ slightly from what is shown in the demo.
+
+
+
+## Further Resources
+
+- [AutoUpdate Plugin][autoupdate-plugin]
+- [Design proposal for update automation][design-proposal]
+- [Project configuration reference][project-config]
+
+[project-config]: ../../reference/project-config.md
+[autoupdate-plugin]: ./../../plugins/available/autoupdate-v1-alpha.md
+[design-proposal]: ./../../../../../designs/update_action.md
+[ai-gh-models]: https://docs.github.com/en/github-models/about-github-models
diff --git a/docs/book/src/reference/completion.md b/docs/book/src/reference/completion.md
index a30635c7e0e..507474bc1f3 100644
--- a/docs/book/src/reference/completion.md
+++ b/docs/book/src/reference/completion.md
@@ -1,35 +1,60 @@
# Enabling shell autocompletion
-The Kubebuilder completion script can be generated with the command `kubebuilder completion [bash|zsh|powershell]`.
-Note that sourcing the completion script in your shell enables Kubebuilder autocompletion.
+The Kubebuilder completion script can be generated with the command `kubebuilder completion [bash|fish|powershell|zsh]`.
+Note that sourcing the completion script in your shell enables Kubebuilder autocompletion.
+
+## Bash
Prerequisites for Bash
-The completion Bash script depends on [bash-completion](https://github.com/scop/bash-completion), which means that you have to install this software first (you can test if you have bash-completion already installed). Also, ensure that your Bash version is 4.1+.
+The completion Bash script depends on [bash-completion](https://github.com/scop/bash-completion), which means that you have to install this software first (you can test if you have bash-completion already installed). Also, ensure that your Bash version is 4.1+.
+
+```bash
+$ bash --version
+```
+- Check that bash is an available shell:
-- Once installed, go ahead and add the path `/usr/local/bin/bash` in the `/etc/shells`.
+ ```bash
+ cat /etc/shells | grep '^.*/bash'
+ ```
- `echo “/usr/local/bin/bash” > /etc/shells`
+- If not, add bash to `/etc/shells`. For example, if bash is at `/usr/local/bin/bash`:
-- Make sure to use installed shell by current user.
+ ```bash
+ echo "/usr/local/bin/bash" >> /etc/shells
+ ```
- `chsh -s /usr/local/bin/bash`
+- Make sure the current user uses bash as their shell.
-- Add following content in /.bash_profile or ~/.bashrc
+ ```bash
+ chsh -s /usr/local/bin/bash
+ ```
-```
-# kubebuilder autocompletion
-if [ -f /usr/local/share/bash-completion/bash_completion ]; then
-. /usr/local/share/bash-completion/bash_completion
-fi
-. <(kubebuilder completion)
-```
-- Restart terminal for the changes to be reflected.
+- Add following content to `~/.bash_profile` or `~/.bashrc`
+
+ ```bash
+ # kubebuilder autocompletion
+ if [ -f /usr/local/share/bash-completion/bash_completion ]; then
+ . /usr/local/share/bash-completion/bash_completion
+ fi
+ . <(kubebuilder completion bash)
+ ```
+
+- Restart terminal for the changes to be reflected or `source` the changed bash file.
+
+ ```bash
+ . ~/.bash_profile
+ ```
+
+## Zsh
-
-Zsh
Follow a similar protocol for `zsh` completion.
-
+
+## Fish
+
+```
+source (kubebuilder completion fish | psub)
+```
diff --git a/docs/book/src/reference/controller-gen.md b/docs/book/src/reference/controller-gen.md
index 1ee7c82002c..97225650ca3 100644
--- a/docs/book/src/reference/controller-gen.md
+++ b/docs/book/src/reference/controller-gen.md
@@ -1,6 +1,6 @@
# controller-gen CLI
-KubeBuilder makes use of a tool called
+Kubebuilder makes use of a tool called
[controller-gen](https://sigs.k8s.io/controller-tools/cmd/controller-gen)
for generating utility code and Kubernetes YAML. This code and config
generation is controlled by the presence of special ["marker
@@ -13,7 +13,7 @@ results).
Both are configured through command line options specified in [marker
format](/reference/markers.md).
-For instance,
+For instance, the following command:
```shell
controller-gen paths=./... crd:trivialVersions=true rbac:roleName=controller-perms output:crd:artifacts:config=config/crd/bases
diff --git a/docs/book/src/reference/envtest.md b/docs/book/src/reference/envtest.md
index 6afe1320b24..757efd7ca6d 100644
--- a/docs/book/src/reference/envtest.md
+++ b/docs/book/src/reference/envtest.md
@@ -1,42 +1,73 @@
# Configuring envtest for integration tests
-The [`controller-runtime/pkg/envtest`][envtest] Go library helps write integration tests for your controllers by setting up and starting an instance of etcd and the Kubernetes API server, without kubelet, controller-manager or other components.
+The [`controller-runtime/pkg/envtest`][envtest] Go library helps write integration tests for your controllers by setting up and starting an instance of etcd and the
+Kubernetes API server, without kubelet, controller-manager or other components.
## Installation
-The `test` make target, also called by the `docker-build` target,
-[downloads][setup-envtest] a set of envtest binaries (described above) to run tests with.
-Typically nothing needs to be done on your part,
-as the download and install script is fully automated,
-although it does require `bash` to run.
+Installing the binaries is as a simple as running `make envtest`. `envtest` will download the Kubernetes API server binaries to the `bin/` folder in your project
+by default. `make test` is the one-stop shop for downloading the binaries, setting up the test environment, and running the tests.
+
+
+You can refer to the Makefile of the Kubebuilder scaffold and observe that the envtest setup is consistently aligned across all controller-runtime releases.Starting from `release-0.19`, it is configured to automatically download the artefact from the correct location, **ensuring that kubebuilder users are not impacted.**
+
+```shell
+## Tool Binaries
+..
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+...
+
+## Tool Versions
+...
+#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}')
+...
+.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)
+ $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
+```
-If you would like to download the tarball containing these binaries,
-to use in a disconnected environment for example,
-run the following (Kubernetes version 1.19.2 is an example version):
+## Installation in Air Gapped/disconnected environments
+If you would like to download the tarball containing the binaries, to use in a disconnected environment you can use
+[`setup-envtest`][setup-envtest] to download the required binaries locally. There are a lot of ways to configure `setup-envtest` to avoid talking to
+the internet you can read about them [here](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest#what-if-i-dont-want-to-talk-to-the-internet).
+The examples below will show how to install the Kubernetes API binaries using mostly defaults set by `setup-envtest`.
-```sh
-K8S_VERSION=1.19.2
-curl -sSLo envtest-bins.tar.gz "https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-${K8S_VERSION}-$(go env GOOS)-$(go env GOARCH).tar.gz"
+### Download the binaries
+`make envtest` will download the `setup-envtest` binary to `./bin/`.
+```shell
+make envtest
```
-Then install them:
-
+Installing the binaries using `setup-envtest` stores the binary in OS specific locations, you can read more about them
+[here](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest#where-does-it-put-all-those-binaries)
```sh
-mkdir /opt/kubebuilder/testbin
-tar -C /opt/kubebuilder/testbin --strip-components=1 -zvxf envtest-bins.tar.gz
+./bin/setup-envtest use 1.31.0
```
-Once these binaries are installed, you can either change the `test` target to:
+### Update the test make target
+Once these binaries are installed, change the `test` make target to include a `-i` like below. `-i` will only check for locally installed
+binaries and not reach out to remote resources. You could also set the `ENVTEST_INSTALLED_ONLY` env variable.
```makefile
test: manifests generate fmt vet
- go test ./... -coverprofile cover.out
+ KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -i --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out
```
-Or configure the existing target to skip the download and point to a [custom location](#environment-variables):
-
+NOTE: The `ENVTEST_K8S_VERSION` needs to match the `setup-envtest` you downloaded above. Otherwise, you will see an error like the below
```sh
-make test SKIP_FETCH_TOOLS=1 KUBEBUILDER_ASSETS=/opt/kubebuilder/testbin
+no such version (1.24.5) exists on disk for this architecture (darwin/amd64) -- try running `list -i` to see what's on disk
```
## Writing tests
@@ -64,31 +95,50 @@ err = testEnv.Stop()
Logs from the test runs are prefixed with `test-env`.
+
+Examples
+
+You can use the plugin [DeployImage](../plugins/available/deploy-image-plugin-v1-alpha.md) to check examples. This plugin allows users to scaffold API/Controllers to deploy and manage an Operand (image) on the cluster following the guidelines and best practices. It abstracts the complexities of achieving this goal while allowing users to customize the generated code.
+
+Therefore, you can check that a test using ENV TEST will be generated for the controller which has the purpose to ensure that the Deployment is created successfully. You can see an example of its code implementation under the `testdata` directory with the [DeployImage](../plugins/available/deploy-image-plugin-v1-alpha.md) [samples here](https://github.com/kubernetes-sigs/kubebuilder/blob/v4.6.0/testdata/project-v4-with-plugins/internal/controller/busybox_controller_test.go).
+
+
+
### Configuring your test control plane
-Controller-runtime’s [envtest](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest) framework requires `kubectl`, `kube-apiserver`, and `etcd` binaries be present locally to simulate the API portions of a real cluster.
+Controller-runtime’s [envtest][envtest] framework requires `kubectl`, `kube-apiserver`, and `etcd` binaries be present locally to simulate the API portions of a real cluster.
-For projects built with plugin v3+ (see your PROJECT file's `layout` key), the `make test` command will install these binaries to the `testbin/` directory and use them when running tests that use `envtest`.
+The `make test` command will install these binaries to the `bin/` directory and use them when running tests that use `envtest`.
+Ie,
+```shell
+./bin/k8s/
+└── 1.25.0-darwin-amd64
+ ├── etcd
+ ├── kube-apiserver
+ └── kubectl
+```
You can use environment variables and/or flags to specify the `kubectl`,`api-server` and `etcd` setup within your integration tests.
### Environment Variables
| Variable name | Type | When to use |
-| --- | :--- | :--- |
+| --- | :--- | :--- |
| `USE_EXISTING_CLUSTER` | boolean | Instead of setting up a local control plane, point to the control plane of an existing cluster. |
-| `KUBEBUILDER_ASSETS` | path to directory | Point integration tests to a directory containing all binaries (api-server, etcd and kubectl). |
-| `TEST_ASSET_KUBE_APISERVER`, `TEST_ASSET_ETCD`, `TEST_ASSET_KUBECTL` | paths to, respectively, api-server, etcd and kubectl binaries | Similar to `KUBEBUILDER_ASSETS`, but more granular. Point integration tests to use binaries other than the default ones. These environment variables can also be used to ensure specific tests run with expected versions of these binaries. |
-| `KUBEBUILDER_CONTROLPLANE_START_TIMEOUT` and `KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT` | durations in format supported by [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) | Specify timeouts different from the default for the test control plane to (respectively) start and stop; any test run that exceeds them will fail. |
-| `KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT` | boolean | Set to `true` to attach the control plane's stdout and stderr to os.Stdout and os.Stderr. This can be useful when debugging test failures, as output will include output from the control plane. |
+| `KUBEBUILDER_ASSETS` | path to directory | Point integration tests to a directory containing all binaries (api-server, etcd and kubectl). |
+| `TEST_ASSET_KUBE_APISERVER`, `TEST_ASSET_ETCD`, `TEST_ASSET_KUBECTL` | paths to, respectively, api-server, etcd and kubectl binaries | Similar to `KUBEBUILDER_ASSETS`, but more granular. Point integration tests to use binaries other than the default ones. These environment variables can also be used to ensure specific tests run with expected versions of these binaries. |
+| `KUBEBUILDER_CONTROLPLANE_START_TIMEOUT` and `KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT` | durations in format supported by [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) | Specify timeouts different from the default for the test control plane to (respectively) start and stop; any test run that exceeds them will fail. |
+| `KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT` | boolean | Set to `true` to attach the control plane's stdout and stderr to os.Stdout and os.Stderr. This can be useful when debugging test failures, as output will include output from the control plane. |
See that the `test` makefile target will ensure that all is properly setup when you are using it. However, if you would like to run the tests without use the Makefile targets, for example via an IDE, then you can set the environment variables directly in the code of your `suite_test.go`:
-```go
+```go
var _ = BeforeSuite(func(done Done) {
- Expect(os.Setenv("TEST_ASSET_KUBE_APISERVER", "../testbin/bin/kube-apiserver")).To(Succeed())
- Expect(os.Setenv("TEST_ASSET_ETCD", "../testbin/bin/etcd")).To(Succeed())
- Expect(os.Setenv("TEST_ASSET_KUBECTL", "../testbin/bin/kubectl")).To(Succeed())
+ Expect(os.Setenv("TEST_ASSET_KUBE_APISERVER", "../bin/k8s/1.25.0-darwin-amd64/kube-apiserver")).To(Succeed())
+ Expect(os.Setenv("TEST_ASSET_ETCD", "../bin/k8s/1.25.0-darwin-amd64/etcd")).To(Succeed())
+ Expect(os.Setenv("TEST_ASSET_KUBECTL", "../bin/k8s/1.25.0-darwin-amd64/kubectl")).To(Succeed())
+ // OR
+ Expect(os.Setenv("KUBEBUILDER_ASSETS", "../bin/k8s/1.25.0-darwin-amd64")).To(Succeed())
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
testenv = &envtest.Environment{}
@@ -107,7 +157,15 @@ var _ = AfterSuite(func() {
Expect(os.Unsetenv("TEST_ASSET_KUBECTL")).To(Succeed())
})
-```
+```
+
+
+ENV TEST Config Options
+
+You can look at the controller-runtime docs to know more about its configuration options, see [here](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest#Environment). On top of that, if you are
+looking to use ENV TEST to test your webhooks then you might want to give a look at its install [options](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest#WebhookInstallOptions).
+
+
### Flags
Here's an example of modifying the flags with which to start the API server in your integration tests, compared to the default values in `envtest.DefaultKubeAPIServerFlags`:
@@ -143,5 +201,158 @@ expectedOwnerReference := v1.OwnerReference{
Expect(deployment.ObjectMeta.OwnerReferences).To(ContainElement(expectedOwnerReference))
```
-[envtest]:https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest
-[setup-envtest]:https://github.com/kubernetes-sigs/controller-runtime/blob/master/hack/setup-envtest.sh
+
+
+Namespace usage limitation
+
+EnvTest does not support namespace deletion. Deleting a namespace will seem to succeed, but the namespace will just be put in a Terminating state, and never actually be reclaimed. Trying to recreate the namespace will fail. This will cause your reconciler to continue reconciling any objects left behind, unless they are deleted.
+
+To overcome this limitation you can create a new namespace for each test. Even so, when one test completes (e.g. in "namespace-1") and another test starts (e.g. in "namespace-2"), the controller will still be reconciling any active objects from "namespace-1". This can be avoided by ensuring that all tests clean up after themselves as part of the test teardown. If teardown of a namespace is difficult, it may be possible to wire the reconciler in such a way that it ignores reconcile requests that come from namespaces other than the one being tested:
+
+```go
+type MyCoolReconciler struct {
+ client.Client
+ ...
+ Namespace string // restrict namespaces to reconcile
+}
+func (r *MyCoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ _ = r.Log.WithValues("myreconciler", req.NamespacedName)
+ // Ignore requests for other namespaces, if specified
+ if r.Namespace != "" && req.Namespace != r.Namespace {
+ return ctrl.Result{}, nil
+ }
+```
+Whenever your tests create a new namespace, it can modify the value of reconciler.Namespace. The reconciler will effectively ignore the previous namespace.
+For further information see the issue raised in the controller-runtime [controller-runtime/issues/880](https://github.com/kubernetes-sigs/controller-runtime/issues/880) to add this support.
+
+
+## Cert-Manager and Prometheus options
+
+Projects scaffolded with Kubebuilder can enable the [`metrics`][metrics] and the [`cert-manager`][cert-manager] options. Note that when we are using the ENV TEST we are looking to test the controllers and their reconciliation. It is considered an integrated test because the ENV TEST API will do the test against a cluster and because of this the binaries are downloaded and used to configure its pre-requirements, however, its purpose is mainly to `unit` test the controllers.
+
+Therefore, to test a reconciliation in common cases you do not need to care about these options. However, if you would like to do tests with the Prometheus and the Cert-manager installed you can add the required steps to install them before running the tests.
+Following an example.
+
+```go
+ // Add the operations to install the Prometheus operator and the cert-manager
+ // before the tests.
+ BeforeEach(func() {
+ By("installing prometheus operator")
+ Expect(utils.InstallPrometheusOperator()).To(Succeed())
+
+ By("installing the cert-manager")
+ Expect(utils.InstallCertManager()).To(Succeed())
+ })
+
+ // You can also remove them after the tests::
+ AfterEach(func() {
+ By("uninstalling the Prometheus manager bundle")
+ utils.UninstallPrometheusOperManager()
+
+ By("uninstalling the cert-manager bundle")
+ utils.UninstallCertManager()
+ })
+```
+
+Check the following example of how you can implement the above operations:
+
+```go
+const (
+ certmanagerVersion = "v1.5.3"
+ certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml"
+
+ defaultKindCluster = "kind"
+ defaultKindBinary = "kind"
+
+ prometheusOperatorVersion = "0.51"
+ prometheusOperatorURL = "https://raw.githubusercontent.com/prometheus-operator/" + "prometheus-operator/release-%s/bundle.yaml"
+)
+
+func warnError(err error) {
+ _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
+}
+
+// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics.
+func InstallPrometheusOperator() error {
+ url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
+ cmd := exec.Command("kubectl", "apply", "-f", url)
+ _, err := Run(cmd)
+ return err
+}
+
+// UninstallPrometheusOperator uninstalls the prometheus
+func UninstallPrometheusOperator() {
+ url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+}
+
+// UninstallCertManager uninstalls the cert manager
+func UninstallCertManager() {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "delete", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ warnError(err)
+ }
+}
+
+// InstallCertManager installs the cert manager bundle.
+func InstallCertManager() error {
+ url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
+ cmd := exec.Command("kubectl", "apply", "-f", url)
+ if _, err := Run(cmd); err != nil {
+ return err
+ }
+ // Wait for cert-manager-webhook to be ready, which can take time if cert-manager
+ //was re-installed after uninstalling on a cluster.
+ cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook",
+ "--for", "condition=Available",
+ "--namespace", "cert-manager",
+ "--timeout", "5m",
+ )
+
+ _, err := Run(cmd)
+ return err
+}
+
+// LoadImageToKindClusterWithName loads a local docker image to the kind cluster
+func LoadImageToKindClusterWithName(name string) error {
+ cluster := defaultKindCluster
+ if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
+ cluster = v
+ }
+ kindOptions := []string{"load", "docker-image", name, "--name", cluster}
+ kindBinary := defaultKindBinary
+ if v, ok := os.LookupEnv("KIND"); ok {
+ kindBinary = v
+ }
+ cmd := exec.Command(kindBinary, kindOptions...)
+ _, err := Run(cmd)
+ return err
+}
+```
+However, see that tests for the metrics and cert-manager might fit better well as e2e tests and not under the tests done using ENV TEST for the controllers. You might want to give a look at the [sample example][sdk-e2e-sample-example] implemented into [Operator-SDK][sdk] repository to know how you can write your e2e tests to ensure the basic workflows of your project.
+Also, see that you can run the tests against a cluster where you have some configurations in place they can use the option to test using an existing cluster:
+
+```go
+testEnv = &envtest.Environment{
+ UseExistingCluster: true,
+}
+```
+
+
+Setup ENV TEST tool
+To know more about the tooling used to configure ENVTEST which is used in the setup-envtest target in the Makefile
+of the projects build with Kubebuilder see the [README][readme]
+of its tooling.
+
+
+[metrics]: https://book.kubebuilder.io/reference/metrics.html
+[envtest]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest
+[setup-envtest]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/tools/setup-envtest
+[cert-manager]: https://book.kubebuilder.io/cronjob-tutorial/cert-manager.html
+[sdk-e2e-sample-example]: https://github.com/operator-framework/operator-sdk/tree/master/testdata/go/v4/memcached-operator/test/e2e
+[sdk]: https://github.com/operator-framework/operator-sdk
+[readme]: https://github.com/kubernetes-sigs/controller-runtime/blob/main/tools/setup-envtest/README.md
diff --git a/docs/book/src/reference/generating-crd.md b/docs/book/src/reference/generating-crd.md
index 70c1eae59de..a5cbbb774a0 100644
--- a/docs/book/src/reference/generating-crd.md
+++ b/docs/book/src/reference/generating-crd.md
@@ -1,6 +1,6 @@
# Generating CRDs
-KubeBuilder uses a tool called [`controller-gen`][controller-tools] to
+Kubebuilder uses a tool called [`controller-gen`][controller-tools] to
generate utility code and Kubernetes object YAML, like
CustomResourceDefinitions.
@@ -10,7 +10,7 @@ packages. In the case of CRDs, these are generally pulled from your
`_types.go` files. For more information on markers, see the [marker
reference docs][marker-ref].
-KubeBuilder provides a `make` target to run controller-gen and generate
+Kubebuilder provides a `make` target to run controller-gen and generate
CRDs: `make manifests`.
When you run `make manifests`, you should see CRDs generated under the
@@ -157,69 +157,44 @@ in your CRD, and use a webhook to convert between them.
For more details on this process, see the [multiversion
tutorial](/multiversion-tutorial/tutorial.md).
-By default, KubeBuilder disables generating different validation for
+By default, Kubebuilder disables generating different validation for
different versions of the Kind in your CRD, to be compatible with older
Kubernetes versions.
You'll need to enable this by switching the line in your makefile that
says `CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false`
-to `CRD_OPTIONS ?= crd:preserveUnknownFields=false`
+to `CRD_OPTIONS ?= crd:preserveUnknownFields=false` if using v1beta CRDs,
+and `CRD_OPTIONS ?= crd` if using v1 (recommended).
Then, you can use the `+kubebuilder:storageversion` [marker][crd-markers]
to indicate the [GVK](/cronjob-tutorial/gvks.md "Group-Version-Kind") that
should be used to store data by the API server.
-### Supporting older cluster versions
-
-By default, `kubebuilder create api` will create CRDs of API version `v1`,
-a version introduced in Kubernetes v1.16. If your project intends to support
-Kubernetes cluster versions older than v1.16, you must use the `v1beta1` API version:
-
-```sh
-kubebuilder create api --crd-version v1beta1 ...
-```
-
-To support Kubernetes clusters of version v1.14 or lower, you'll also need to
-remove the controller-gen option `preserveUnknownFields=false` from your Makefile.
-This is done by switching the line that says
-`CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false`
-to `CRD_OPTIONS ?= crd:trivialVersions=true`
-
-
-
-`v1beta1` is deprecated and will be removed in Kubernetes v1.22, so upgrading is recommended.
-
-
-
## Under the hood
-KubeBuilder scaffolds out make rules to run `controller-gen`. The rules
+Kubebuilder scaffolds out make rules to run `controller-gen`. The rules
will automatically install controller-gen if it's not on your path using
-`go get` with Go modules.
+`go install` with Go modules.
You can also run `controller-gen` directly, if you want to see what it's
doing.
Each controller-gen "generator" is controlled by an option to
-controller-gen, using the same syntax as markers. For instance, to
-generate CRDs with "trivial versions" (no version conversion webhooks), we
-call `controller-gen crd:trivialVersions=true paths=./api/...`.
-
-controller-gen also supports different output "rules" to control how
-and where output goes. Notice the `manifests` make rule (condensed
-slightly to only generate CRDs):
+controller-gen, using the same syntax as markers. controller-gen
+also supports different output "rules" to control how and where output goes.
+Notice the `manifests` make rule (condensed slightly to only generate CRDs):
```makefile
# Generate manifests for CRDs
manifests: controller-gen
- $(CONTROLLER_GEN) crd:trivialVersions=true paths="./..." output:crd:artifacts:config=config/crd/bases
+ $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
```
It uses the `output:crd:artifacts` output rule to indicate that
CRD-related config (non-code) artifacts should end up in
`config/crd/bases` instead of `config/crd`.
-To see all the options for `controller-gen`, run
+To see all the options including generators for `controller-gen`, run
```shell
$ controller-gen -h
@@ -237,7 +212,7 @@ $ controller-gen -hhh
[openapi-schema]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject "OpenAPI v3"
-[kube-additional-printer-colums]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#additional-printer-columns "Custom Resource Definitions: Additional Printer Columns"
+[kube-additional-printer-columns]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#additional-printer-columns "Custom Resource Definitions: Additional Printer Columns"
[kube-subresources]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#status-subresource "Custom Resource Definitions: Status Subresource"
diff --git a/docs/book/src/reference/good-practices.md b/docs/book/src/reference/good-practices.md
new file mode 100644
index 00000000000..325c9b03ce7
--- /dev/null
+++ b/docs/book/src/reference/good-practices.md
@@ -0,0 +1,77 @@
+# Good Practices
+
+## What is "Reconciliation" in Operators?
+
+When you create a project using Kubebuilder, see the scaffolded code generated under `cmd/main.go`. This code initializes a [Manager][controller-runtime-manager], and the project relies on the [controller-runtime][controller-runtime] framework. The Manager manages [Controllers][controllers], which offer a reconcile function that synchronizes resources until the desired state is achieved within the cluster.
+
+Reconciliation is an ongoing loop that executes necessary operations to maintain the desired state, adhering to Kubernetes principles, such as the [control loop][k8s-control-loop]. For further information, check out the [Operator patterns][k8s-operator-pattern] documentation from Kubernetes to better understand those concepts.
+
+## Why should reconciliations be idempotent?
+
+When developing operators, the controller’s reconciliation loop needs to be idempotent. By following the [Operator pattern][operator-pattern] we create [controllers][controllers] that provide a reconcile function responsible for synchronizing resources until the desired state is reached on the cluster. Developing idempotent solutions will allow the reconciler to correctly respond to generic or unexpected events, easily deal with application startup or upgrade. More explanation on this is available [here][controller-runtime-topic].
+
+Writing reconciliation logic according to specific events, breaks the recommendation of operator pattern and goes against the design principles of [controller-runtime][controller-runtime]. This may lead to unforeseen consequences, such as resources becoming stuck and requiring manual intervention.
+
+## Understanding Kubernetes APIs and following API conventions
+
+Building your operator commonly involves extending the Kubernetes API itself. It is helpful to understand precisely how Custom Resource Definitions (CRDs) interact with the Kubernetes API. Also, the [Kubebuilder documentation][docs] on Groups and Versions and Kinds may be helpful to understand these concepts better as they relate to operators.
+
+Additionally, we recommend checking the documentation on [Operator patterns][operator-pattern] from Kubernetes to better understand the purpose of the standard solutions built with KubeBuilder.
+
+## Why you should adhere to the Kubernetes API conventions and standards
+
+Embracing the [Kubernetes API conventions and standards][k8s-api-conventions] is crucial for maximizing the potential of your applications and deployments. By adhering to these established practices, you can benefit in several ways.
+
+Firstly, adherence ensures seamless interoperability within the Kubernetes ecosystem. Following conventions allows your applications to work harmoniously with other components, reducing compatibility issues and promoting a consistent user experience.
+
+Secondly, sticking to API standards enhances the maintainability and troubleshooting of your applications. Adopting familiar patterns and structures makes debugging and supporting your deployments easier, leading to more efficient operations and quicker issue resolution.
+
+Furthermore, leveraging the Kubernetes API conventions empowers you to harness the platform's full capabilities. By working within the defined framework, you can leverage the rich set of features and resources offered by Kubernetes, enabling scalability, performance optimization, and resilience.
+
+Lastly, embracing these standards future-proofs your native solutions. By aligning with the evolving Kubernetes ecosystem, you ensure compatibility with future updates, new features, and enhancements introduced by the vibrant Kubernetes community.
+
+In summary, by adhering to the Kubernetes API conventions and standards, you unlock the potential for seamless integration, simplified maintenance, optimal performance, and future-readiness, all contributing to the success of your applications and deployments.
+
+## Why should one avoid a system design where a single controller is responsible for managing multiple CRDs (Custom Resource Definitions)(for example, an _'install_all_controller.go'_)?
+
+Avoid a design solution where the same controller reconciles more than one Kind. Having many Kinds (such as CRDs), that are all managed by the same controller, usually goes against the design proposed by controller-runtime. Furthermore, this might hurt concepts such as encapsulation, the Single Responsibility Principle, and Cohesion. Damaging these concepts may cause unexpected side effects and increase the difficulty of extending, reusing, or maintaining the operator.
+Having one controller manage many Custom Resources (CRs) in an Operator can lead to several issues:
+
+- **Complexity**: A single controller managing multiple CRs can increase the complexity of the code, making it harder to understand, maintain, and debug.
+- **Scalability**: Each controller typically manages a single kind of CR for scalability. If a single controller handles multiple CRs, it could become a bottleneck, reducing the overall efficiency and responsiveness of your system.
+- **Single Responsibility Principle**: Following this principle from software engineering, each controller should ideally have only one job. This approach simplifies development and debugging, and makes the system more robust.
+- **Error Isolation**: If one controller manages multiple CRs and an error occurs, it could potentially impact all the CRs it manages. Having a single controller per CR ensures that an issue with one controller or CR does not directly affect others.
+- **Concurrency and Synchronization**: A single controller managing multiple CRs could lead to race conditions and require complex synchronization, especially if the CRs have interdependencies.
+
+In conclusion, while it might seem efficient to have a single controller manage multiple CRs, it often leads to higher complexity, lower scalability, and potential stability issues. It's generally better to adhere to the single responsibility principle, where each CR is managed by its own controller.
+
+## Why You Should Adopt Status Conditions
+
+We recommend you manage your solutions using Status Conditionals following the [K8s Api conventions][k8s-api-conventions] because:
+
+- **Standardization**: Conditions provide a standardized way to represent the state of an Operator's custom resources, making it easier for users and tools to understand and interpret the resource's status.
+- **Readability**: Conditions can clearly express complex states by using a combination of multiple conditions, making it easier for users to understand the current state and progress of the resource.
+- **Extensibility**: As new features or states are added to your Operator, conditions can be easily extended to represent these new states without requiring significant changes to the existing API or structure.
+- **Observability**: Status conditions can be monitored and tracked by cluster administrators and external monitoring tools, enabling better visibility into the state of the custom resources managed by the Operator.
+- **Compatibility**: By adopting the common pattern of using conditions in Kubernetes APIs, Operator authors ensure their custom resources align with the broader ecosystem, which helps users to have a consistent experience when interacting with multiple Operators and resources in their clusters.
+
+
+ Example of Usage
+
+Check out the [Deploy Image Plugin][deploy-image]. This plugin allows users to scaffold API/Controllers to deploy and manage an Operand (image) on the cluster following the guidelines and best practices. It abstracts the
+complexities of achieving this goal while allowing users to customize the generated code.
+
+Therefore, you can check an example of Status Conditional usage by looking at its API(s) scaffolded and code implemented under the Reconciliation into its Controllers.
+
+
+
+[docs]: /cronjob-tutorial/gvks.html
+[operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
+[controllers]: https://kubernetes.io/docs/concepts/architecture/controller/
+[controller-runtime-topic]: https://github.com/kubernetes-sigs/controller-runtime/blob/main/FAQ.md#q-how-do-i-have-different-logic-in-my-reconciler-for-different-types-of-events-eg-create-update-delete
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[deploy-image]: /plugins/available/deploy-image-plugin-v1-alpha.md
+[controller-runtime-manager]: https://github.com/kubernetes-sigs/controller-runtime/blob/304027bcbe4b3f6d582180aec5759eb4db3f17fd/pkg/manager/manager.go#L53
+[k8s-api-conventions]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
+[k8s-control-loop]: https://kubernetes.io/docs/concepts/architecture/controller/
+[k8s-operator-pattern]: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
diff --git a/docs/book/src/reference/images/pprof-result-visualization.png b/docs/book/src/reference/images/pprof-result-visualization.png
new file mode 100644
index 00000000000..a50fd1e4064
Binary files /dev/null and b/docs/book/src/reference/images/pprof-result-visualization.png differ
diff --git a/docs/book/src/reference/kind-config.yaml b/docs/book/src/reference/kind-config.yaml
index bd587fe4f0c..f4bf78cb4a4 100644
--- a/docs/book/src/reference/kind-config.yaml
+++ b/docs/book/src/reference/kind-config.yaml
@@ -1,7 +1,7 @@
kind: Cluster
-apiVersion: kind.sigs.k8s.io/v1alpha3
+apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- - role: worker
\ No newline at end of file
+ - role: worker
diff --git a/docs/book/src/reference/kind.md b/docs/book/src/reference/kind.md
index d72f20ace77..f918646b48c 100644
--- a/docs/book/src/reference/kind.md
+++ b/docs/book/src/reference/kind.md
@@ -1,4 +1,11 @@
-# Kind Cluster
+# Using Kind For Development Purposes and CI
+
+## Why Use Kind
+
+- **Fast Setup:** Launch a multi-node Kubernetes cluster locally in under a minute.
+- **Quick Teardown:** Dismantle the cluster in just a few seconds, streamlining your development workflow.
+- **Local Image Usage:** Deploy your container images directly without the need to push to a remote registry.
+- **Lightweight and Efficient:** Kind is a minimalistic Kubernetes distribution, making it perfect for local development and CI/CD pipelines.
This only cover the basics to use a kind cluster. You can find more details at
[kind documentation](https://kind.sigs.k8s.io/).
@@ -24,7 +31,7 @@ For example, the following is a sample `kind` configuration.
```
Using the configuration above, run the following command will give you a k8s
-v1.17.2 cluster with 1 master and 3 workers.
+v1.17.2 cluster with 1 control-plane node and 3 worker nodes.
```bash
kind create cluster --config hack/kind-config.yaml --image=kindest/node:v1.17.2
@@ -32,22 +39,21 @@ kind create cluster --config hack/kind-config.yaml --image=kindest/node:v1.17.2
You can use `--image` flag to specify the cluster version you want, e.g.
`--image=kindest/node:v1.17.2`, the supported version are listed
-[here](https://hub.docker.com/r/kindest/node/tags)
+[here](https://hub.docker.com/r/kindest/node/tags).
## Load Docker Image into the Cluster
When developing with a local kind cluster, loading docker images to the cluster
is a very useful feature. You can avoid using a container registry.
-- [Load a local image into a kind cluster](https://kind.sigs.k8s.io/docs/user/quick-start/#loading-an-image-into-your-cluster).
-
```bash
kind load docker-image your-image-name:your-tag
```
+See [Load a local image into a kind cluster](https://kind.sigs.k8s.io/docs/user/quick-start/#loading-an-image-into-your-cluster) for more information.
+
## Delete a Cluster
-- Delete a kind cluster
```bash
kind delete cluster
```
diff --git a/docs/book/src/reference/makefile-helpers.md b/docs/book/src/reference/makefile-helpers.md
deleted file mode 100644
index 091a5206112..00000000000
--- a/docs/book/src/reference/makefile-helpers.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Makefile Helpers
-
-By default, the projects are scaffolded with a `Makefile`. You can customize and update this file as please you. Here, you will find some helpers that can be useful.
-
-## To debug with go-delve
-
-The projects are built with Go and you have a lot of ways to do that. One of the options would be use [go-delve](https://github.com/go-delve/delve) for it:
-
-```sh
-# Run with Delve for development purposes against the configured Kubernetes cluster in ~/.kube/config
-# Delve is a debugger for the Go programming language. More info: https://github.com/go-delve/delve
-run-delve: generate fmt vet manifests
- go build -gcflags "all=-trimpath=$(shell go env GOPATH)" -o bin/manager main.go
- dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./bin/manager
-```
-
-## To change the version of CRDs
-
-The `controller-gen` program (from [controller-tools](https://github.com/kubernetes-sigs/controller-tools))
-generates CRDs for kubebuilder projects, wrapped in the following `make` rule:
-
-```sh
-manifests: controller-gen
- $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
-```
-
-`controller-gen` lets you specify what CRD API version to generate (either "v1", the default, or "v1beta1").
-You can direct it to generate a specific version by adding `crd:crdVersions={}` to your `CRD_OPTIONS`,
-found at the top of your Makefile:
-
-```sh
-CRD_OPTIONS ?= "crd:crdVersions={v1beta1},trivialVersions=true,preserveUnknownFields=false"
-```
-
-## To get all the manifests without deploying
-
-By adding `make dry-run` you can get the patched manifests in the dry-run folder, unlike `make depĺoy` which runs `kustomize` and `kubectl apply`.
-
-To accomplish this, add the following lines to the Makefile:
-
-```make
-dry-run: manifests
- cd config/manager && kustomize edit set image controller=${IMG}
- mkdir -p dry-run
- kustomize build config/default > dry-run/manifests.yaml
-```
diff --git a/docs/book/src/reference/markers.md b/docs/book/src/reference/markers.md
index 39544196a4c..0520717b0f2 100644
--- a/docs/book/src/reference/markers.md
+++ b/docs/book/src/reference/markers.md
@@ -1,6 +1,6 @@
# Markers for Config/Code Generation
-KubeBuilder makes use of a tool called
+Kubebuilder makes use of a tool called
[controller-gen](/reference/controller-gen.md) for
generating utility code and Kubernetes YAML. This code and config
generation is controlled by the presence of special "marker comments" in
@@ -15,12 +15,25 @@ a marker name, optionally followed by some marker specific configuration:
// +kubebuilder:printcolumn:JSONPath=".status.replicas",name=Replicas,type=string
```
+
+difference between // +optional and // +kubebuilder:validation:Optional
+
+Controller-gen supports both (see the output of `controller-gen crd -www`). `+kubebuilder:validation:Optional` and `+optional` can be applied to fields.
+
+But `+kubebuilder:validation:Optional` can also be applied at the package level such that it applies to every field in the package.
+
+If you're using controller-gen only then they're redundant, but if you're using other generators or you want developers that need to build their own clients for your API, you'll want to also include `+optional`.
+
+The most reliable way in 1.x to get `+optional` is `omitempty`.
+
+
+
See each subsection for information about different types of code and YAML
generation.
-## Generating Code & Artifacts in KubeBuilder
+## Generating Code & Artifacts in Kubebuilder
-KubeBuilder projects have two `make` targets that make use of
+Kubebuilder projects have two `make` targets that make use of
controller-gen:
- `make manifests` generates Kubernetes object YAML, like
@@ -89,5 +102,5 @@ each key and value is separated by a colon (`:`), and each key-value
pair is separated by a comma:
```go
-// +kubebuilder:validation:Default={magic: {numero: 42, stringified: forty-two}}
+// +kubebuilder:default={magic: {numero: 42, stringified: forty-two}}
```
diff --git a/docs/book/src/reference/markers/crd-validation.md b/docs/book/src/reference/markers/crd-validation.md
index 3307cc6b5d1..9064bc10432 100644
--- a/docs/book/src/reference/markers/crd-validation.md
+++ b/docs/book/src/reference/markers/crd-validation.md
@@ -6,4 +6,16 @@ schema option.
See [Generating CRDs](/reference/generating-crd.md) for examples.
+
+Understanding Marker Grouping in Documentation
+
+Certain markers may seem duplicated. However, these markers are grouped based on their context of use
+— such as fields, types, or arrays. For instance, a marker like `+kubebuilder:validation:Enum` can be applied to
+individual fields or array items, and this flexibility is reflected in the documentation.
+
+The grouping ensures clarity by showing how the same marker can be reused for different purposes.
+
+
+
+
{{#markerdocs CRD validation}}
diff --git a/docs/book/src/reference/markers/crd.md b/docs/book/src/reference/markers/crd.md
index 284c2044915..71846cbae51 100644
--- a/docs/book/src/reference/markers/crd.md
+++ b/docs/book/src/reference/markers/crd.md
@@ -4,6 +4,6 @@ These markers describe how to construct a custom resource definition from
a series of Go types and packages. Generation of the actual validation
schema is described by the [validation markers](./crd-validation.md).
-See [Generating CRDs](/reference/generating-crd.md) for examples.
+See [Generating CRDs](../generating-crd.md) for examples.
{{#markerdocs CRD}}
diff --git a/docs/book/src/reference/markers/scaffold.md b/docs/book/src/reference/markers/scaffold.md
new file mode 100644
index 00000000000..2068a77b9d8
--- /dev/null
+++ b/docs/book/src/reference/markers/scaffold.md
@@ -0,0 +1,177 @@
+# Scaffold
+
+The `+kubebuilder:scaffold` marker is a key part of the Kubebuilder scaffolding system. It marks locations in generated
+files where additional code will be injected as new resources (such as controllers, webhooks, or APIs) are scaffolded.
+This enables Kubebuilder to seamlessly integrate newly generated components into the project without affecting
+user-defined code.
+
+
+ If you delete or change the `+kubebuilder:scaffold` markers
+
+The Kubebuilder CLI specifically looks for these markers in expected
+files during code generation. If the marker is moved or removed, the CLI will
+not be able to inject the necessary code, and the scaffolding process may
+fail or behave unexpectedly.
+
+
+
+## How It Works
+
+When you scaffold a new resource using the Kubebuilder CLI (e.g., `kubebuilder create api`),
+the CLI identifies `+kubebuilder:scaffold` markers in key locations and uses them as placeholders
+to insert the required imports and registration code.
+
+## Example Usage in `main.go`
+
+Here is how the `+kubebuilder:scaffold` marker is used in a typical `main.go` file. To illustrate how it works, consider the following command to create a new API:
+
+```shell
+kubebuilder create api --group crew --version v1 --kind Admiral --controller=true --resource=true
+```
+
+### To Add New Imports
+
+The `+kubebuilder:scaffold:imports` marker allows the Kubebuilder CLI to inject additional imports,
+such as for new controllers or webhooks. When we create a new API, the CLI automatically adds the required import paths
+in this section.
+
+For example, after creating the `Admiral` API in a single-group layout,
+the CLI will add `crewv1 "/api/v1"` to the imports:
+
+```go
+import (
+ "crypto/tls"
+ "flag"
+ "os"
+
+ // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
+ // to ensure that exec-entrypoint and run can make use of them.
+ _ "k8s.io/client-go/plugin/pkg/client/auth"
+ ...
+ crewv1 "sigs.k8s.io/kubebuilder/testdata/project-v4/api/v1"
+ // +kubebuilder:scaffold:imports
+)
+```
+
+### To Register a New Scheme
+
+The `+kubebuilder:scaffold:scheme` marker is used to register newly created API versions with the runtime scheme,
+ensuring the API types are recognized by the manager.
+
+For example, after creating the Admiral API, the CLI will inject the
+following code into the `init()` function to register the scheme:
+
+
+```go
+func init() {
+ ...
+ utilruntime.Must(crewv1.AddToScheme(scheme))
+ // +kubebuilder:scaffold:scheme
+}
+```
+
+## To Set Up a Controller
+
+When we create a new controller (e.g., for Admiral), the Kubebuilder CLI injects the controller
+setup code into the manager using the `+kubebuilder:scaffold:builder` marker. This marker indicates where
+the setup code for new controllers should be added.
+
+For example, after creating the `AdmiralReconciler`, the CLI will add the following code
+to register the controller with the manager:
+
+```go
+if err = (&crewv1.AdmiralReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+}).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "Admiral")
+ os.Exit(1)
+}
+// +kubebuilder:scaffold:builder
+```
+
+The `+kubebuilder:scaffold:builder` marker ensures that newly scaffolded controllers are
+properly registered with the manager, so that the controller can reconcile the resource.
+
+## List of `+kubebuilder:scaffold` Markers
+
+| Marker | Usual Location | Function |
+|--------------------------------------------------------------------------------|------------------------------|---------------------------------------------------------------------------------|
+| `+kubebuilder:scaffold:imports` | `main.go` | Marks where imports for new controllers, webhooks, or APIs should be injected. |
+| `+kubebuilder:scaffold:scheme` | `init()` in `main.go` | Used to add API versions to the scheme for runtime. |
+| `+kubebuilder:scaffold:builder` | `main.go` | Marks where new controllers should be registered with the manager. |
+| `+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: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. |
+| `+kubebuilder:scaffold:e2e-metrics-webhooks-readiness` | `test/e2e` | Adds readiness logic so metrics e2e tests wait for webhook service endpoints before creating pods. |
+
+ **(No longer supported)** `+kubebuilder:scaffold:crdkustomizecainjectionpatch`
+
+If you find this marker in your code please:
+
+1. **Remove the CERTMANAGER Section from `config/crd/kustomization.yaml`:**
+
+ Delete the `CERTMANAGER` section to prevent unintended CA injection patches for CRDs. Ensure the following lines are removed or commented out:
+
+ ```yaml
+ # [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_firstmates.yaml
+ # +kubebuilder:scaffold:crdkustomizecainjectionpatch
+ ```
+
+2. **Ensure CA Injection Configuration in `config/default/kustomization.yaml`:**
+
+ Under the `[CERTMANAGER]` replacement in `config/default/kustomization.yaml`, add the following code for proper CA injection generation:
+
+ **NOTE:** You must ensure that the code contains the following target markers:
+ - `+kubebuilder:scaffold:crdkustomizecainjectionns`
+ - `+kubebuilder:scaffold:crdkustomizecainjectioname`
+
+ ```yaml
+ # - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
+ # kind: Certificate
+ # group: cert-manager.io
+ # version: v1
+ # name: serving-cert # This name should match the one in certificate.yaml
+ # fieldPath: .metadata.namespace # Namespace of the certificate CR
+ # 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
+ # fieldPath: .metadata.name
+ # targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
+ # +kubebuilder:scaffold:crdkustomizecainjectioname
+ ```
+
+3. **Ensure Only Conversion Webhook Patches in `config/crd/patches`:**
+
+ The `config/crd/patches` directory and the corresponding entries in `config/crd/kustomization.yaml` should only contain files for conversion webhooks. Previously, a bug caused the patch file to be generated for any webhook, but only patches for webhooks scaffolded with the `--conversion` option should be included.
+
+For further guidance, you can refer to examples in the `testdata/` directory in the Kubebuilder repository.
+
+> **Alternatively**: You can use the [`alpha generate`](./../rescaffold.md) command to re-generate the project from scratch
+> using the latest release available. Afterward, you can re-add only your code implementation on top to ensure your project
+> includes all the latest bug fixes and enhancements.
+
+
+
+
+Creating Your Own Markers
+
+If you are using Kubebuilder as a library to create [your own plugins](./../../plugins/creating-plugins.md) and extend its CLI functionalities,
+you have the flexibility to define and use your own markers. To implement your own markers, refer to the [kubebuilder/v4/pkg/machinery](https://pkg.go.dev/sigs.k8s.io/kubebuilder/v4/pkg/machinery),
+which provides tools to create and manage markers effectively.
+
+
+
+
+
diff --git a/docs/book/src/reference/metrics-reference.md b/docs/book/src/reference/metrics-reference.md
new file mode 100644
index 00000000000..4e437a17658
--- /dev/null
+++ b/docs/book/src/reference/metrics-reference.md
@@ -0,0 +1,23 @@
+# Default Exported Metrics References
+
+Following the metrics which are exported and provided by [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) by default:
+
+| Metrics name | Type | Description |
+| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [workqueue_depth](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L41) | Gauge | Current depth of workqueue. |
+| [workqueue_adds_total](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L47) | Counter | Total number of adds handled by workqueue. |
+| [workqueue_queue_duration_seconds](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L53) | Histogram | How long in seconds an item stays in workqueue before being requested. |
+| [workqueue_work_duration_seconds](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L60) | Histogram | How long in seconds processing an item from workqueue takes. |
+| [workqueue_unfinished_work_seconds](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L67) | Gauge | How many seconds of work has been done that is in progress and hasn't been observed by work_duration. Large values indicate stuck threads. One can deduce the number of stuck threads by observing the rate at which this increases. |
+| [workqueue_longest_running_processor_seconds](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L76) | Gauge | How many seconds has the longest running processor for workqueue been running. |
+| [workqueue_retries_total](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/workqueue.go#L83) | Counter | Total number of retries handled by workqueue. |
+| [rest_client_requests_total ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/metrics/client_go_adapter.go#L33) | Counter | Number of HTTP requests, partitioned by status code, method, and host. |
+| [controller_runtime_reconcile_total ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/internal/controller/metrics/metrics.go#L30) | Counter | Total number of reconciliations per controller. |
+| [controller_runtime_reconcile_errors_total ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/internal/controller/metrics/metrics.go#L37) | Counter | Total number of reconciliation errors per controller. |
+| [controller_runtime_terminal_reconcile_errors_total ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/internal/controller/metrics/metrics.go#L44) | Counter | Total number of terminal errors from the reconciler. |
+| [controller_runtime_reconcile_time_seconds ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/internal/controller/metrics/metrics.go#L51) | Histogram | Length of time per reconciliation per controller. |
+| [controller_runtime_max_concurrent_reconciles ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/internal/controller/metrics/metrics.go#L60) | Gauge | Maximum number of concurrent reconciles per controller. |
+| [controller_runtime_active_workers ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/internal/controller/metrics/metrics.go#L67) | Gauge | Number of currently used workers per controller. |
+| [controller_runtime_webhook_latency_seconds ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/webhook/internal/metrics/metrics.go#L31) | Histogram | Histogram of the latency of processing admission requests. |
+| [controller_runtime_webhook_requests_total ](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/webhook/internal/metrics/metrics.go#L40) | Counter | Total number of admission requests by HTTP status code. |
+| [controller_runtime_webhook_requests_in_flight](https://github.com/kubernetes-sigs/controller-runtime/blob/v0.16.3/pkg/webhook/internal/metrics/metrics.go#L51) | Gauge | Current number of admission requests being served. |
diff --git a/docs/book/src/reference/metrics.md b/docs/book/src/reference/metrics.md
index 78f3862e884..a93e21f9c30 100644
--- a/docs/book/src/reference/metrics.md
+++ b/docs/book/src/reference/metrics.md
@@ -1,24 +1,370 @@
# Metrics
By default, controller-runtime builds a global prometheus registry and
-publishes a collection of performance metrics for each controller.
+publishes [a collection of performance metrics](/reference/metrics-reference.md) for each controller.
-## Protecting the Metrics
-These metrics are protected by [kube-auth-proxy](https://github.com/brancz/kube-rbac-proxy)
-by default if using kubebuilder. Kubebuilder v2.2.0+ scaffold a clusterrole which
-can be found at `config/rbac/auth_proxy_client_clusterrole.yaml`.
+
+ IMPORTANT: If you are using `kube-rbac-proxy`
-You will need to grant permissions to your Prometheus server so that it can
-scrape the protected metrics. To achieve that, you can create a
-`clusterRoleBinding` to bind the `clusterRole` to the service account that your
-Prometheus server uses. If you are using `kube-prometheus`, this cluster binding already exists.
+Please stop using the image `gcr.io/kubebuilder/kube-rbac-proxy` as soon as possible.
+Your projects will be affected and may fail to work if the image cannot be pulled.
-You can run the following kubectl command to create it. If using kubebuilder
-`` is the `namePrefix` field in `config/default/kustomization.yaml`.
+**Images provided under `gcr.io/kubebuilder/` will be unavailable from early 2025.**
+
+- **Projects initialized with Kubebuilder versions `v3.14` or lower** utilize [kube-rbac-proxy](https://github.com/brancz/kube-rbac-proxy) to protect the metrics endpoint.
+In this case, you might want to upgrade your project to the latest release or ensure that you have applied the same or similar code changes.
+
+- **However, projects initialized with Kubebuilder versions `v4.1.0` or higher** have similar protection using `authn/authz`
+enabled by default via Controller-Runtime's feature [WithAuthenticationAndAuthorization](https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/filters#WithAuthenticationAndAuthorization).
+
+If you want to continue using [kube-rbac-proxy](https://github.com/brancz/kube-rbac-proxy) then you MUST change
+your project to use the image from another source.
+
+> For further information, see: [kubebuilder/discussions/3907](https://github.com/kubernetes-sigs/kubebuilder/discussions/3907)
+
+
+
+## Metrics Configuration
+
+By looking at the file `config/default/kustomization.yaml` you can
+check the metrics are exposed by default:
+
+```yaml
+# [METRICS] Expose the controller manager metrics service.
+- metrics_service.yaml
+```
+
+```yaml
+patches:
+ # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443.
+ # More info: https://book.kubebuilder.io/reference/metrics
+ - path: manager_metrics_patch.yaml
+ target:
+ kind: Deployment
+```
+
+Then, you can check in the `cmd/main.go` where metrics server
+is configured:
+
+```go
+// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
+// For more info: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/metrics/server
+Metrics: metricsserver.Options{
+ ...
+},
+```
+
+## Consuming Controller Metrics in Kubebuilder
+
+You can consume the metrics exposed by the controller using the `curl`
+command or any other HTTP client such as Prometheus.
+
+However, before doing so, ensure that your client has the
+**required RBAC permissions** to access the `/metrics` endpoint.
+
+### Granting Permissions to Access Metrics
+
+Kubebuilder scaffolds a `ClusterRole` with the necessary read permissions under:
+
+```
+config/rbac/metrics_reader_role.yaml
+```
+
+This file contains the required RBAC rules to allow access to the metrics endpoint.
+
+
+This ClusterRole is only a helper
+
+Kubebuilder **does not scaffold a RoleBinding or ClusterRoleBinding by default.**
+This is an intentional design choice to avoid:
+
+- Accidentally binding to the wrong service account,
+- Granting access in restricted environments,
+- Creating conflicts in multi-team or multi-tenant clusters.
+
+
+
+#### Create a ClusterRoleBinding
+
+You can create the binding via `kubectl`:
+
+```bash
+kubectl create clusterrolebinding metrics \
+ --clusterrole=-metrics-reader \
+ --serviceaccount=:
+```
+
+Or with a manifest:
+
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: allow-metrics-access
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: metrics-reader
+subjects:
+- kind: ServiceAccount
+ name: controller-manager
+ namespace: system # Replace 'system' with your controller-manager's namespace
+```
+
+
+Why this is manual:
+
+Kubebuilder avoids scaffolding RoleBindings by default because it might:
+ - Bind to the wrong ServiceAccount
+ - Grant access unnecessarily
+ - Cause problems in restricted or multi-tenant clusters
+ - This design provides safety and flexibility, but requires manual binding.
+
+
+### Testing the Metrics Endpoint (via Curl Pod)
+
+If you'd like to manually test access to the metrics endpoint, follow these steps:
+
+- Create Role Binding
```bash
-kubectl create clusterrolebinding metrics --clusterrole=-metrics-reader --serviceaccount=:
+kubectl create clusterrolebinding -metrics-binding \
+ --clusterrole=-metrics-reader \
+ --serviceaccount=-system:-controller-manager
+```
+
+- Generate a Token
+
+```bash
+export TOKEN=$(kubectl create token -controller-manager -n -system)
+echo $TOKEN
+```
+
+- Launch Curl Pod
+
+```bash
+kubectl run curl-metrics --rm -it --restart=Never \
+ --image=curlimages/curl:7.87.0 -n -system -- /bin/sh
+```
+
+- Call Metrics Endpoint
+
+Inside the pod, use:
+
+```bash
+curl -v -k -H "Authorization: Bearer $TOKEN" \
+ https://-controller-manager-metrics-service.-system.svc.cluster.local:8443/metrics
+```
+
+
+Notes
+
+- Replace ``, ``, and `` accordingly.
+- Ensure TLS is enabled and certificates are valid if not skipping verification (`-k`).
+
+Check the options to protect your metrics endpoint in the next sections.
+
+
+## Metrics Protection and available options
+
+Unprotected metrics endpoints can expose valuable data to unauthorized users,
+such as system performance, application behavior, and potentially confidential
+operational metrics. This exposure can lead to security vulnerabilities
+where an attacker could gain insights into the system's operation
+and exploit weaknesses.
+
+### By using authn/authz (Enabled by default)
+
+To mitigate these risks, Kubebuilder projects utilize authentication (authn) and authorization (authz) to protect the
+metrics endpoint. This approach ensures that only authorized users and service accounts can access sensitive metrics
+data, enhancing the overall security of the system.
+
+In the past, the [kube-rbac-proxy](https://github.com/brancz/kube-rbac-proxy) was employed to provide this protection.
+However, its usage has been discontinued in recent versions. Since the release of `v4.1.0`, projects have had the
+metrics endpoint enabled and protected by default using the [WithAuthenticationAndAuthorization](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/metrics/server)
+feature provided by controller-runtime.
+
+Therefore, you will find the following configuration:
+
+- In the `cmd/main.go`:
+
+```go
+if secureMetrics {
+ ...
+ metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
+}
+```
+
+This configuration leverages the FilterProvider to enforce authentication and authorization on the metrics endpoint.
+By using this method, you ensure that the endpoint is accessible only to those with the appropriate permissions.
+
+- In the `config/rbac/kustomization.yaml`:
+
+```yaml
+# The following RBAC configurations are used to protect
+# the metrics endpoint with authn/authz. These configurations
+# ensure that only authorized users and service accounts
+# can access the metrics endpoint.
+- metrics_auth_role.yaml
+- metrics_auth_role_binding.yaml
+- metrics_reader_role.yaml
+```
+
+In this way, only Pods using the `ServiceAccount` token are authorized to read the metrics endpoint. For example:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: metrics-consumer
+ namespace: system
+spec:
+ # Use the scaffolded service account name to allow authn/authz
+ serviceAccountName: controller-manager
+ containers:
+ - name: metrics-consumer
+ image: curlimages/curl:latest
+ command: ["/bin/sh"]
+ args:
+ - "-c"
+ - >
+ while true;
+ do
+ # Note here that we are passing the token obtained from the ServiceAccount to curl the metrics endpoint
+ curl -s -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
+ https://controller-manager-metrics-service.system.svc.cluster.local:8443/metrics;
+ sleep 60;
+ done
+```
+
+### **(Recommended)** Enabling certificates for Production (Disabled by default)
+
+
+Why Is This Not Enabled by Default?
+
+This option is not enabled by default because it introduces a dependency on CertManager.
+To keep the project as lightweight and beginner-friendly as possible, it is disabled by default.
+
+
+
+
+Recommended for Production
+
+The default scaffold in `cmd/main.go` uses a **controller-runtime feature** to
+automatically generate a self-signed certificate to secure the metrics server.
+While this is convenient for development and testing, it is **not** recommended
+for production.
+
+Those certificates are used to secure the transport layer (TLS).
+The token authentication using `authn/authz`, which is enabled by default serves
+as the application-level credential. However, for example, when you enable
+the integration of your metrics with Prometheus, those certificates can be used
+to secure the communication.
+
+
+
+Projects built with Kubebuilder releases `4.4.0` and above have the logic scaffolded
+to enable the usage of certificates managed by [CertManager](https://cert-manager.io/)
+for securing the metrics server. Following the steps below, you can configure your
+project to use certificates managed by CertManager.
+
+1. **Enable Cert-Manager in `config/default/kustomization.yaml`:**
+ - Uncomment the cert-manager resource to include it in your project:
+
+ ```yaml
+ - ../certmanager
+ ```
+
+2. **Enable the Patch to configure the usage of the certs in the Controller Deployment in `config/default/kustomization.yaml`:**
+ - Uncomment the `cert_metrics_manager_patch.yaml` to mount the `serving-cert` secret in the Manager Deployment.
+
+ ```yaml
+ # 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
+ ```
+3. **Enable the CertManager replaces for the Metrics Server certificates in `config/default/kustomization.yaml`:**
+ - Uncomment the replacements block bellow. It is required to properly set the DNS names for the certificates configured under `config/certmanager`.
+
+ ```yaml
+ # [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
+ #
+ ```
+
+4. **Enable the Patch for the `ServiceMonitor` to Use the Cert-Manager-Managed Secret `config/prometheus/kustomization.yaml`:**
+ - Add or uncomment the `ServiceMonitor` patch to securely reference the cert-manager-managed secret, replacing insecure configurations with secure certificate verification:
+
+ ```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
+ ```
+
+ > **NOTE** that the `ServiceMonitor` patch above will ensure that if you enable the Prometheus integration,
+ it will securely reference the certificates created and managed by CertManager. But it will **not** enable the
+ integration with Prometheus. To enable the integration with Prometheus, you need uncomment the `#- ../certmanager`
+ in the `config/default/kustomization.yaml`. For more information, see [Exporting Metrics for Prometheus](#exporting-metrics-for-prometheus).
+
+### **(Optional)** By using Network Policy (Disabled by default)
+
+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 `authn/authz`.
+
+Uncomment the following line in the `config/default/kustomization.yaml`:
+
+```
+# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
+# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
+# Only CR(s) which uses webhooks and applied on namespaces labeled 'webhooks: enabled' will be able to work properly.
+#- ../network-policy
```
## Exporting Metrics for Prometheus
@@ -26,11 +372,12 @@ kubectl create clusterrolebinding metrics --clusterrole=-metrics
Follow the steps below to export the metrics using the Prometheus Operator:
1. Install Prometheus and Prometheus Operator.
-We recommend using [kube-prometheus](https://github.com/coreos/kube-prometheus#installing)
-in production if you don't have your own monitoring system.
-If you are just experimenting, you can only install Prometheus and Prometheus Operator.
+ We recommend using [kube-prometheus](https://github.com/coreos/kube-prometheus#installing)
+ in production if you don't have your own monitoring system.
+ If you are just experimenting, you can only install Prometheus and Prometheus Operator.
+
2. Uncomment the line `- ../prometheus` in the `config/default/kustomization.yaml`.
-It creates the `ServiceMonitor` resource which enables exporting the metrics.
+ It creates the `ServiceMonitor` resource which enables exporting the metrics.
```yaml
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
@@ -47,6 +394,20 @@ NAME AGE
monitor-controller-manager-metrics-monitor 2m8s
```
+
+If you are using Prometheus Operator ensure that you have the required
+permissions
+
+If you are using Prometheus Operator, be aware that, by default, its RBAC
+rules are only enabled for the `default` and `kube-system namespaces`. See its
+guide to know [how to configure kube-prometheus to monitor other namespaces using the `.jsonnet` file](https://github.com/prometheus-operator/kube-prometheus/blob/main/docs/monitoring-other-namespaces.md).
+
+Alternatively, you can give the Prometheus Operator permissions to monitor other namespaces using RBAC. See the Prometheus Operator
+[Enable RBAC rules for Prometheus pods](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/getting-started.md#enable-rbac-rules-for-prometheus-pods)
+documentation to know how to enable the permissions on the namespace where the
+`ServiceMonitor` and manager exist.
+
+
Also, notice that the metrics are exported by default through port `8443`. In this way,
you are able to check the Prometheus metrics in its dashboard. To verify it, search
for the metrics exported from the namespace where the project is running
@@ -60,7 +421,7 @@ If you wish to publish additional metrics from your controllers, this
can be easily achieved by using the global registry from
`controller-runtime/pkg/metrics`.
-One way to achieve this is to declare your collectors as global variables and then register them using `init()`.
+One way to achieve this is to declare your collectors as global variables and then register them using `init()` in the controller's package.
For example:
@@ -74,7 +435,7 @@ var (
goobers = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "goobers_total",
- Help: "Number of goobers proccessed",
+ Help: "Number of goobers processed",
},
)
gooberFailures = prometheus.NewCounter(
@@ -92,5 +453,31 @@ func init() {
```
You may then record metrics to those collectors from any part of your
-reconcile loop, and those metrics will be available for prometheus or
+reconcile loop. These metrics can be evaluated from anywhere in the operator code.
+
+
+Enabling metrics in Prometheus UI
+
+In order to publish metrics and view them on the Prometheus UI, the Prometheus instance would have to be configured to select the Service Monitor instance based on its labels.
+
+
+
+Those metrics will be available for prometheus or
other openmetrics systems to scrape.
+
+
+
+
+Controller-Runtime Auth/Authz Feature Current Known Limitations and Considerations
+
+Some known limitations and considerations have been identified. The settings for `cache TTL`, `anonymous access`, and
+`timeouts` are currently hardcoded, which may lead to performance and security concerns due to the inability to
+fine-tune these parameters. Additionally, the current implementation lacks support for configurations like
+`alwaysAllow` for critical paths (e.g., `/healthz`) and `alwaysAllowGroups` (e.g., `system:masters`), potentially
+causing operational challenges. Furthermore, the system heavily relies on stable connectivity to the `kube-apiserver`,
+making it vulnerable to metrics outages during network instability. This can result in the loss of crucial metrics data,
+particularly during critical periods when monitoring and diagnosing issues in real-time is essential.
+
+An [issue](https://github.com/kubernetes-sigs/controller-runtime/issues/2781) has been opened to
+enhance the controller-runtime and address these considerations.
+
\ No newline at end of file
diff --git a/docs/book/src/reference/platform.md b/docs/book/src/reference/platform.md
new file mode 100644
index 00000000000..429efa20afc
--- /dev/null
+++ b/docs/book/src/reference/platform.md
@@ -0,0 +1,223 @@
+# Platforms Supported
+
+Kubebuilder produces solutions that by default can work on multiple platforms or specific ones, depending on how you
+build and configure your workloads. This guide aims to help you properly configure your projects according to your needs.
+
+## Overview
+
+To provide support on specific or multiple platforms, you must ensure that all images used in workloads are built to
+support the desired platforms. Note that they may not be the same as the platform where you develop your solutions and use KubeBuilder, but instead the platform(s) where your solution should run and be distributed.
+It is recommended to build solutions that work on multiple platforms so that your project works
+on any Kubernetes cluster regardless of the underlying operating system and architecture.
+
+## How to define which platforms are supported
+
+The following covers what you need to do to provide the support for one or more platforms or architectures.
+
+### 1) Build workload images to provide the support for other platform(s)
+
+The images used in workloads such as in your Pods/Deployments will need to provide the support for this other platform.
+You can inspect the images using a ManifestList of supported platforms using the command
+[docker manifest inspect ][docker-manifest], i.e.:
+
+```shell
+$ docker manifest inspect myregistry/example/myimage:v0.0.1
+{
+ "schemaVersion": 2,
+ "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
+ "manifests": [
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "size": 739,
+ "digest": "sha256:a274a1a2af811a1daf3fd6b48ff3d08feb757c2c3f3e98c59c7f85e550a99a32",
+ "platform": {
+ "architecture": "arm64",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "size": 739,
+ "digest": "sha256:d801c41875f12ffd8211fffef2b3a3d1a301d99f149488d31f245676fa8bc5d9",
+ "platform": {
+ "architecture": "amd64",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "size": 739,
+ "digest": "sha256:f4423c8667edb5372fb0eafb6ec599bae8212e75b87f67da3286f0291b4c8732",
+ "platform": {
+ "architecture": "s390x",
+ "os": "linux"
+ }
+ },
+ {
+ "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
+ "size": 739,
+ "digest": "sha256:621288f6573c012d7cf6642f6d9ab20dbaa35de3be6ac2c7a718257ec3aff333",
+ "platform": {
+ "architecture": "ppc64le",
+ "os": "linux"
+ }
+ },
+ ]
+}
+```
+
+### 2) (Recommended as a Best Practice) Ensure that node affinity expressions are set to match the supported platforms
+
+Kubernetes provides a mechanism called [nodeAffinity][node-affinity] which can be used to limit the possible node
+targets where a pod can be scheduled. This is especially important to ensure correct scheduling behavior in clusters
+with nodes that span across multiple platforms (i.e. heterogeneous clusters).
+
+**Kubernetes manifest example**
+
+```yaml
+affinity:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: kubernetes.io/arch
+ operator: In
+ values:
+ - amd64
+ - arm64
+ - ppc64le
+ - s390x
+ - key: kubernetes.io/os
+ operator: In
+ values:
+ - linux
+```
+
+**Golang Example**
+
+```go
+Template: corev1.PodTemplateSpec{
+ ...
+ Spec: corev1.PodSpec{
+ Affinity: &corev1.Affinity{
+ NodeAffinity: &corev1.NodeAffinity{
+ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
+ NodeSelectorTerms: []corev1.NodeSelectorTerm{
+ {
+ MatchExpressions: []corev1.NodeSelectorRequirement{
+ {
+ Key: "kubernetes.io/arch",
+ Operator: "In",
+ Values: []string{"amd64"},
+ },
+ {
+ Key: "kubernetes.io/os",
+ Operator: "In",
+ Values: []string{"linux"},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ SecurityContext: &corev1.PodSecurityContext{
+ ...
+ },
+ Containers: []corev1.Container{{
+ ...
+ }},
+ },
+```
+
+
+ Example(s)
+
+You can look for some code examples by checking the code which is generated via the Deploy
+Image plugin. ([More info](../plugins/available/deploy-image-plugin-v1-alpha.md))
+
+
+
+## Producing projects that support multiple platforms
+
+You can use [`docker buildx`][buildx] to cross-compile via emulation ([QEMU](https://www.qemu.org/)) to build the manager image.
+See that projects scaffold with the latest versions of Kubebuilder have the Makefile target `docker-buildx`.
+
+**Example of Usage**
+
+```shell
+$ make docker-buildx IMG=myregistry/myoperator:v0.0.1
+```
+
+Note that you need to ensure that all images and workloads required and used by your project will provide the same
+support as recommended above, and that you properly configure the [nodeAffinity][node-affinity] for all your workloads.
+Therefore, ensure that you uncomment the following code in the `config/manager/manager.yaml` file
+
+```yaml
+# TODO(user): Uncomment the following code to configure the nodeAffinity expression
+# according to the platforms which are supported by your solution.
+# It is considered best practice to support multiple architectures. You can
+# build your manager image using the makefile target docker-buildx.
+# affinity:
+# nodeAffinity:
+# requiredDuringSchedulingIgnoredDuringExecution:
+# nodeSelectorTerms:
+# - matchExpressions:
+# - key: kubernetes.io/arch
+# operator: In
+# values:
+# - amd64
+# - arm64
+# - ppc64le
+# - s390x
+# - key: kubernetes.io/os
+# operator: In
+# values:
+# - linux
+```
+
+
+Building images for releases
+
+You will probably want to automate the releases of your projects to ensure that the images are always built for the
+same platforms. Note that Goreleaser also supports [docker buildx][buildx]. See its [documentation][goreleaser-buildx] for more detail.
+
+Also, you may want to configure GitHub Actions, Prow jobs, or any other solution that you use to build images to
+provide multi-platform support. Note that you can also use other options like `docker manifest create` to customize
+your solutions to achieve the same goals with other tools.
+
+By using Docker and the target provided by default you should NOT change the Dockerfile to use
+any specific GOOS and GOARCH to build the manager binary. However, if you are looking for to
+customize the default scaffold and create your own implementations you might want to give
+a look in the Golang [doc](https://go.dev/doc/install/source#environment) to knows
+its available options.
+
+
+
+## Which (workload) images are created by default?
+
+Projects created with the Kubebuilder CLI have two workloads which are:
+
+### Manager
+
+The container to run the manager implementation is configured in the `config/manager/manager.yaml` file.
+This image is built with the Dockerfile file scaffolded by default and contains the binary of the project \
+which will be built via the command `go build -a -o manager main.go`.
+
+Note that when you run `make docker-build` OR `make docker-build IMG=myregistry/myprojectname:`
+an image will be built from the client host (local environment) and produce an image for
+the client os/arch, which is commonly linux/amd64 or linux/arm64.
+
+
+Mac Os
+
+If you are running from an Mac Os environment then, Docker also will consider it as linux/$arch. Be aware that
+when, for example, is running Kind on a Mac OS operational system the nodes will
+end up labeled with ` kubernetes.io/os=linux`
+
+
+
+[node-affinity]: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity
+[docker-manifest]: https://docs.docker.com/engine/reference/commandline/manifest/
+[buildx]: https://docs.docker.com/build/buildx/
+[goreleaser-buildx]: https://goreleaser.com/customization/docker/#use-a-specific-builder-with-docker-buildx
diff --git a/docs/book/src/reference/pprof-tutorial.md b/docs/book/src/reference/pprof-tutorial.md
new file mode 100644
index 00000000000..e4b4b2a0dfd
--- /dev/null
+++ b/docs/book/src/reference/pprof-tutorial.md
@@ -0,0 +1,62 @@
+# Monitoring Performance with Pprof
+
+[Pprof][github], a Go profiling tool, helps identify performance bottlenecks in areas like CPU and memory usage. It's integrated with the controller-runtime library's HTTP server, enabling profiling via HTTP endpoints. You can visualize the data using go tool pprof. Since [Pprof][github] is built into controller-runtime, no separate installation is needed. [Manager options][manager-options-doc] make it easy to enable pprof and gather runtime metrics to optimize controller performance.
+
+
+ Not Recommended for Production
+
+While [Pprof][github] is an excellent tool for profiling and debugging, it is not recommended to leave it enabled in production environments. The primary reasons are:
+
+1. **Security Risk**: The profiling endpoints expose detailed information about your application's performance and resource usage, which could be exploited if accessed by unauthorized users.
+2. **Overhead**: Running profiling can introduce performance overhead, mainly CPU usage, especially under heavy load, potentially impacting production workloads.
+
+
+
+## How to use Pprof?
+
+1. **Enabling Pprof**
+
+ In your `cmd/main.go` file, add the field:
+
+ ```golang
+ mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ ...
+ // PprofBindAddress is the TCP address that the controller should bind to
+ // for serving pprof. Specify the manager address and the port that should be bind.
+ PprofBindAddress: ":8082",
+ ...
+ })
+ ```
+
+2. **Test It Out**
+
+ After enabling [Pprof][github], you need to build and deploy your controller to test it out. Follow the steps in the [Quick Start guide][quick-start-run-it] to run your project locally or on a cluster.
+
+ Then, you can apply your CRs/samples in order to monitor the performance of its controllers.
+
+3. **Exporting the data**
+
+ Using `curl`, export the profiling statistics to a file like this:
+
+ ```bash
+ # Note that we are using the bind host and port configured via the
+ # Manager Options in the cmd/main.go
+ curl -s "http://127.0.0.1:8082/debug/pprof/profile" > ./cpu-profile.out
+ ```
+
+4. **Visualizing the results on Browser**
+
+ ```bash
+ # Go tool will open a session on port 8080.
+ # You can change this as per your own need.
+ go tool pprof -http=:8080 ./cpu-profile.out
+ ```
+
+ Visualization results will vary depending on the deployed workload, and the Controller's behavior.
+ However, you'll see the result on your browser similar to this one:
+
+ 
+
+[manager-options-doc]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/manager
+[quick-start-run-it]: ../quick-start.md#test-it-out
+[github]: https://github.com/google/pprof
\ No newline at end of file
diff --git a/docs/book/src/reference/project-config.md b/docs/book/src/reference/project-config.md
index c72174888c6..39e4f47d45e 100644
--- a/docs/book/src/reference/project-config.md
+++ b/docs/book/src/reference/project-config.md
@@ -2,7 +2,83 @@
## Overview
-The Project Config represents the configuration of a Kubebuilder project. All projects that are scaffolded with the CLI will generate the `PROJECT` file in the projects' root directory.
+The Project Config represents the configuration of a KubeBuilder project. All projects that are scaffolded with the CLI (KB version 3.0 and higher) will generate the `PROJECT` file in the projects' root directory. Therefore, it will store all plugins and input data used to generate the project and APIs to better enable plugins to make useful decisions when scaffolding.
+
+## Example
+
+Following is an example of a PROJECT config file which is the result of a project generated with two APIs using the [Deploy Image Plugin][deploy-image-plugin].
+
+```yaml
+# Code generated by tool. DO NOT EDIT.
+# This file is used to track the info used to scaffold your project
+# and allow the plugins properly work.
+# More info: https://book.kubebuilder.io/reference/project-config.html
+domain: testproject.org
+cliVersion: v4.6.0
+layout:
+ - go.kubebuilder.io/v4
+plugins:
+ deploy-image.go.kubebuilder.io/v1-alpha:
+ resources:
+ - domain: testproject.org
+ group: example.com
+ kind: Memcached
+ options:
+ containerCommand: memcached,--memory-limit=64,-o,modern,-v
+ containerPort: "11211"
+ image: memcached:1.4.36-alpine
+ runAsUser: "1001"
+ version: v1alpha1
+ - domain: testproject.org
+ group: example.com
+ kind: Busybox
+ options:
+ image: busybox:1.28
+ version: v1alpha1
+projectName: project-v4-with-deploy-image
+repo: sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image
+resources:
+ - api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: testproject.org
+ group: example.com
+ kind: Memcached
+ path: sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image/api/v1alpha1
+ version: v1alpha1
+ webhooks:
+ validation: true
+ webhookVersion: v1
+ - api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: testproject.org
+ group: example.com
+ kind: Busybox
+ path: sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image/api/v1alpha1
+ version: v1alpha1
+ - controller: true
+ domain: io
+ external: true
+ group: cert-manager
+ kind: Certificate
+ path: github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1
+ module: github.com/cert-manager/cert-manager@v1.18.2
+ version: v1
+version: "3"
+```
+## Why do we need to store the plugins and data used?
+
+Following some examples of motivations to track the input used:
+- check if a plugin can or cannot be scaffolded on top of an existing plugin (i.e.) plugin compatibility while chaining multiple of them together.
+- what operations can or cannot be done such as verify if the layout allow API(s) for different groups to be scaffolded for the current configuration or not.
+- verify what data can or not be used in the CLI operations such as to ensure that WebHooks can only be created for pre-existent API(s)
+
+Note that KubeBuilder is not only a CLI tool but can also be used as a library to allow users to create their plugins/tools, provide helpers and customizations on top of their existing projects - an example of which is [Operator-SDK][operator-sdk]. SDK leverages KubeBuilder to create plugins to allow users to work with other languages and provide helpers for their users to integrate their projects with, for example, the [Operator Framework solutions/OLM][olm]. You can check the [plugin's documentation][plugins-doc] to know more about creating custom plugins.
+
+Additionally, another motivation for the PROJECT file is to help us to create a feature that allows users to easily upgrade their projects by providing helpers that automatically re-scaffold the project. By having all the required metadata regarding the APIs, their configurations and versions in the PROJECT file. For example, it can be used to automate the process of re-scaffolding while migrating between plugin versions. ([More info][doc-design-helper]).
## Versioning
@@ -14,58 +90,98 @@ The `PROJECT` version `3` layout looks like:
```yaml
domain: testproject.org
+cliVersion: v4.6.0
layout:
-- go.kubebuilder.io/v3
+ - go.kubebuilder.io/v4
plugins:
- declarative.go.kubebuilder.io/v1:
+ deploy-image.go.kubebuilder.io/v1-alpha:
resources:
- - domain: testproject.org
- group: crew
- kind: FirstMate
- version: v1
-projectName: example
-repo: sigs.k8s.io/kubebuilder/example
+ - domain: testproject.org
+ group: example.com
+ kind: Memcached
+ options:
+ containerCommand: memcached,--memory-limit=64,-o,modern,-v
+ containerPort: "11211"
+ image: memcached:memcached:1.6.26-alpine3.19
+ runAsUser: "1001"
+ version: v1alpha1
+ - domain: testproject.org
+ group: example.com
+ kind: Busybox
+ options:
+ image: busybox:1.36.1
+ version: v1alpha1
+projectName: project-v4-with-deploy-image
+repo: sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image
resources:
-- api:
- crdVersion: v1
- namespaced: true
- controller: true
- domain: testproject.org
- group: crew
- kind: Captain
- path: sigs.k8s.io/kubebuilder/example/api/v1
- version: v1
- webhooks:
- defaulting: true
- validation: true
- webhookVersion: v1
+ - api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: testproject.org
+ group: example.com
+ kind: Memcached
+ path: sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image/api/v1alpha1
+ version: v1alpha1
+ webhooks:
+ validation: true
+ webhookVersion: v1
+ - api:
+ crdVersion: v1
+ namespaced: true
+ controller: true
+ domain: testproject.org
+ group: example.com
+ kind: Busybox
+ path: sigs.k8s.io/kubebuilder/testdata/project-v4-with-deploy-image/api/v1alpha1
+ version: v1alpha1
+ - controller: true
+ domain: io
+ external: true
+ group: cert-manager
+ kind: Certificate
+ path: github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1
+ module: github.com/cert-manager/cert-manager@v1.18.2
+ version: v1
+version: "3"
```
Now let's check its layout fields definition:
-| Field | Description |
-|----------|-------------|
-| `layout` | Defines the global plugins, e.g. a project `init` with `--plugins="go/v3,declarative"` means that any sub-command used will always call its implementation for both plugins in a chain. |
-| `domain` | Store the domain of the project. This information can be provided by the user when the project is generate with the `init` sub-command and the `domain` flag. |
-| `plugins` | Defines the plugins used to do custom scaffolding, e.g. to use the optional `declarative` plugin to do scaffolding for just a specific api via the command `kubebuider create api [options] --plugins=declarative/v1`. |
-| `projectName` | The name of the project. This will be used to scaffold the manager data. By default it is the name of the project directory, however, it can be provided by the user in the `init` sub-command via the `--project-name` flag. |
-| `repo` | The project repository which is the Golang module, e.g `github.com/example/myproject-operator`. |
-| `resources` | An array of all resources which were scaffolded in the project. |
-| `resources.api` | The API scaffolded in the project via the sub-command `create api`. |
-| `resources.api.crdVersion` | The Kubernetes API version (`apiVersion`) used to do the scaffolding for the CRD resource. |
-| `resources.api.namespaced` | The API RBAC permissions which can be namespaced or cluster scoped. |
-| `resources.controller` | Indicates whether a controller was scaffolded for the API. |
-| `resources.domain` | The domain of the resource which is provided by the `--domain` flag when the sub-command `create api` is used. |
-| `resources.group` | The GKV group of the resource which is provided by the `--group` flag when the sub-command `create api` is used. |
-| `resources.version` | The GKV version of the resource which is provided by the `--version` flag when the sub-command `create api` is used. |
-| `resources.kind` | Store GKV Kind of the resource which is provided by the `--kind` flag when the sub-command `create api` is used. |
-| `resources.path` | The import path for the API resource. It will be `/api/` unless the API added to the project is an external or core-type. For the core-types scenarios, the paths used are mapped [here][core-types]. |
-| `resources.webhooks`| Store the webhooks data when the sub-command `create webhook` is used. |
-| `resources.webhooks.webhookVersion` | The Kubernetes API version (`apiVersion`) used to scaffold the webhook resource. |
-| `resources.webhooks.conversion` | It is `true` when the the webhook was scaffold with the `--conversion` flag which means that is a conversion webhook. |
-| `resources.webhooks.defaulting` | It is `true` when the the webhook was scaffold with the `--defaulting` flag which means that is a defaulting webhook. |
-| `resources.webhooks.validation` | It is `true` when the the webhook was scaffold with the `--programmatic-validation` flag which means that is a validation webhook. |
+| Field | Description |
+|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `cliVersion` | Used to record the specific CLI version used during project scaffolding with `init`. Helps identifying the version of the tooling employed, aiding in troubleshooting and ensuring compatibility with updates. |
+| `layout` | Defines the global plugins, e.g. a project `init` with `--plugins="go/v4,deploy-image/v1-alpha"` means that any sub-command used will always call its implementation for both plugins in a chain. |
+| `domain` | Store the domain of the project. This information can be provided by the user when the project is generate with the `init` sub-command and the `domain` flag. |
+| `plugins` | Defines the plugins used to do custom scaffolding, e.g. to use the optional `deploy-image/v1-alpha` plugin to do scaffolding for just a specific api via the command `kubebuider create api [options] --plugins=deploy-image/v1-alpha`. |
+| `projectName` | The name of the project. This will be used to scaffold the manager data. By default it is the name of the project directory, however, it can be provided by the user in the `init` sub-command via the `--project-name` flag. |
+| `repo` | The project repository which is the Golang module, e.g `github.com/example/myproject-operator`. |
+| `resources` | An array of all resources which were scaffolded in the project. |
+| `resources.api` | The API scaffolded in the project via the sub-command `create api`. |
+| `resources.api.crdVersion` | The Kubernetes API version (`apiVersion`) used to do the scaffolding for the CRD resource. |
+| `resources.api.namespaced` | The API RBAC permissions which can be namespaced or cluster scoped. |
+| `resources.controller` | Indicates whether a controller was scaffolded for the API. |
+| `resources.domain` | The domain of the resource which was provided by the `--domain` flag when the project was initialized or via the flag `--external-api-domain` when it was used to scaffold controllers for an [External Type][external-type]. |
+| `resources.group` | The GKV group of the resource which is provided by the `--group` flag when the sub-command `create api` is used. |
+| `resources.version` | The GKV version of the resource which is provided by the `--version` flag when the sub-command `create api` is used. |
+| `resources.kind` | Store GKV Kind of the resource which is provided by the `--kind` flag when the sub-command `create api` is used. |
+| `resources.path` | The import path for the API resource. It will be `/api/` unless the API added to the project is an external or core-type. For the core-types scenarios, the paths used are mapped [here][core-types]. Or either the path informed by the flag `--external-api-path` |
+| `resources.core` | It is `true` when the group used is from Kubernetes API and the API resource is not defined on the project. |
+| `resources.external` | It is `true` when the flag `--external-api-path` was used to generated the scaffold for an [External Type][external-type]. |
+| `resources.module` | **(Optional)** The Go module path for external API dependencies, optionally including a version (e.g., `github.com/cert-manager/cert-manager@v1.18.2` or just `github.com/cert-manager/cert-manager`). Only used when `external` is `true`. Provided via the `--external-api-module` flag to explicitly pin a specific version in `go.mod` or to specify the module when it cannot be automatically determined from `--external-api-path`. If not provided, `go mod tidy` will resolve the dependency automatically. |
+| `resources.webhooks` | Store the webhooks data when the sub-command `create webhook` is used. |
+| `resources.webhooks.spoke` | Store the API version that will act as the Spoke with the designated Hub version for conversion webhooks. |
+| `resources.webhooks.webhookVersion` | The Kubernetes API version (`apiVersion`) used to scaffold the webhook resource. |
+| `resources.webhooks.conversion` | It is `true` when the webhook was scaffold with the `--conversion` flag which means that is a conversion webhook. |
+| `resources.webhooks.defaulting` | It is `true` when the webhook was scaffold with the `--defaulting` flag which means that is a defaulting webhook. |
+| `resources.webhooks.validation` | It is `true` when the webhook was scaffold with the `--programmatic-validation` flag which means that is a validation webhook. |
[project]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/testdata/project-v3/PROJECT
[versioning]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/VERSIONING.md#Versioning
-[core-types]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/golang/options.go
\ No newline at end of file
+[core-types]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/pkg/plugins/golang/options.go
+[deploy-image-plugin]: ../plugins/available/deploy-image-plugin-v1-alpha.md
+[olm]: https://olm.operatorframework.io/
+[plugins-doc]: ../plugins/creating-plugins.html#why-use-the-kubebuilder-style
+[doc-design-helper]: https://github.com/kubernetes-sigs/kubebuilder/blob/master/designs/helper_to_upgrade_projects_by_rescaffolding.md
+[operator-sdk]: https://sdk.operatorframework.io/
+[external-type]: ./using_an_external_resource.md
\ No newline at end of file
diff --git a/docs/book/src/reference/raising-events.md b/docs/book/src/reference/raising-events.md
new file mode 100644
index 00000000000..0364d2775de
--- /dev/null
+++ b/docs/book/src/reference/raising-events.md
@@ -0,0 +1,112 @@
+# Creating Events
+
+It is often useful to publish *Event* objects from the controller Reconcile function as they allow users or any automated processes to see what is going on with a particular object and respond to them.
+
+ Recent Events for an object can be viewed by running `$ kubectl describe `. Also, they can be checked by running `$ kubectl get events`.
+
+
+Events should be raised in certain circumstances only
+
+Be aware that it is **not** recommended to emit Events for all operations. If authors raise too many events, it brings bad UX experiences for those consuming the solutions on the cluster, and they may find it difficult to filter an actionable event from the cluster. For more information, please take a look at the [Kubernetes APIs convention][Events].
+
+
+
+## Writing Events
+
+Anatomy of an Event:
+
+```go
+Event(object runtime.Object, eventtype, reason, message string)
+```
+
+- `object` is the object this event is about.
+- `eventtype` is this event type, and is either *Normal* or *Warning*. ([More info][Event-Example])
+- `reason` is the reason this event is generated. It should be short and unique with `UpperCamelCase` format. The value could appear in *switch* statements by automation. ([More info][Reason-Example])
+- `message` is intended to be consumed by humans. ([More info][Message-Example])
+
+
+
+
+Example Usage
+
+Following is an example of a code implementation that raises an Event.
+
+```go
+ // The following implementation will raise an event
+ r.Recorder.Eventf(cr, "Warning", "Deleting",
+ "Custom Resource %s is being deleted from the namespace %s",
+ cr.Name, cr.Namespace)
+```
+
+
+
+### How to be able to raise Events?
+
+Following are the steps with examples to help you raise events in your controller's reconciliations.
+Events are published from a Controller using an [EventRecorder][Events]`type CorrelatorOptions struct`,
+which can be created for a Controller by calling `GetRecorder(name string)` on a Manager. See that we will change the implementation scaffolded in `cmd/main.go`:
+
+```go
+ if err := (&controller.MyKindReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ // Note that we added the following line:
+ Recorder: mgr.GetEventRecorderFor("mykind-controller"),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "MyKind")
+ os.Exit(1)
+ }
+```
+
+### Allowing usage of EventRecorder on the Controller
+
+To raise an event, you must have access to `record.EventRecorder` in the Controller. Therefore, firstly let's update the controller implementation:
+```go
+import (
+ ...
+ "k8s.io/client-go/tools/record"
+ ...
+)
+// MyKindReconciler reconciles a MyKind object
+type MyKindReconciler struct {
+ client.Client
+ Scheme *runtime.Scheme
+ // See that we added the following code to allow us to pass the record.EventRecorder
+ Recorder record.EventRecorder
+}
+```
+### Passing the EventRecorder to the Controller
+
+Events are published from a Controller using an [EventRecorder]`type CorrelatorOptions struct`,
+which can be created for a Controller by calling `GetRecorder(name string)` on a Manager. See that we will change the implementation scaffolded in `cmd/main.go`:
+
+```go
+ if err := (&controller.MyKindReconciler{
+ Client: mgr.GetClient(),
+ Scheme: mgr.GetScheme(),
+ // Note that we added the following line:
+ Recorder: mgr.GetEventRecorderFor("mykind-controller"),
+ }).SetupWithManager(mgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "MyKind")
+ os.Exit(1)
+ }
+```
+
+### Granting the required permissions
+
+You must also grant the RBAC rules permissions to allow your project to create Events. Therefore, ensure that you add the [RBAC][rbac-markers] into your controller:
+
+```go
+...
+// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
+...
+func (r *MyKindReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+```
+
+And then, run `$ make manifests` to update the rules under `config/rbac/role.yaml`.
+
+[Events]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#events
+[Event-Example]: https://github.com/kubernetes/api/blob/6c11c9e4685cc62e4ddc8d4aaa824c46150c9148/core/v1/types.go#L6019-L6024
+[Reason-Example]: https://github.com/kubernetes/api/blob/6c11c9e4685cc62e4ddc8d4aaa824c46150c9148/core/v1/types.go#L6048
+[Message-Example]: https://github.com/kubernetes/api/blob/6c11c9e4685cc62e4ddc8d4aaa824c46150c9148/core/v1/types.go#L6053
+[rbac-markers]: ./markers/rbac.md
diff --git a/docs/book/src/reference/reference.md b/docs/book/src/reference/reference.md
index 12465e0b207..6228ccdfa8a 100644
--- a/docs/book/src/reference/reference.md
+++ b/docs/book/src/reference/reference.md
@@ -5,6 +5,11 @@
Finalizers are a mechanism to
execute any custom logic related to a resource before it gets deleted from
Kubernetes cluster.
+ - [Watching Resources](watching-resources.md)
+ Watch resources in the Kubernetes cluster to be informed and take actions on changes.
+ - [Watching Secondary Resources that are `Owned` ](watching-resources/secondary-owned-resources.md)
+ - [Watching Secondary Resources that are NOT `Owned`](watching-resources/secondary-resources-not-owned)
+ - [Using Predicates to Refine Watches](watching-resources/predicates-with-watch.md)
- [Kind cluster](kind.md)
- [What's a webhook?](webhook-overview.md)
Webhooks are HTTP callbacks, there are 3
@@ -21,11 +26,18 @@
- [Webhook](markers/webhook.md)
- [Object/DeepCopy](markers/object.md)
- [RBAC](markers/rbac.md)
+ - [Scaffold](markers/scaffold.md)
+ - [Monitoring with Pprof](pprof-tutorial.md)
- [controller-gen CLI](controller-gen.md)
- [completion](completion.md)
- [Artifacts](artifacts.md)
- - [Writing controller tests](writing-tests.md)
+ - [Platform Support](platform.md)
+
+ - [Sub-Module Layouts](submodule-layouts.md)
+ - [Using an external Resource / API](using_an_external_resource.md)
+
- [Metrics](metrics.md)
- - [Makefile Helpers](makefile-helpers.md)
- - [CLI plugins](../plugins/cli-plugins.md)
+ - [Reference](metrics-reference.md)
+
+ - [CLI plugins](../plugins/plugins.md)
diff --git a/docs/book/src/reference/scopes.md b/docs/book/src/reference/scopes.md
new file mode 100644
index 00000000000..7b504f2e434
--- /dev/null
+++ b/docs/book/src/reference/scopes.md
@@ -0,0 +1,118 @@
+# Understanding and Setting Scopes for Managers (Operators) and CRDs
+
+This section covers the configuration of the operational and resource scopes
+within a Kubebuilder project. Managers("Operators") in Kubernetes can be scoped to either
+specific namespaces or the entire cluster, influencing how resources are watched and managed.
+
+Additionally, CustomResourceDefinitions (CRDs) can be defined to be either
+namespace-scoped or cluster-scoped, affecting their availability
+across the cluster.
+
+## Configuring Manager Scope
+
+Managers can operate under different scopes depending on
+the resources they need to handle:
+
+### (Default) Watching All Namespaces
+
+By default, if no namespace is specified, the manager will observe all namespaces.
+This is configured as follows:
+
+```go
+mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+...
+})
+```
+
+### Watching a Single Namespace
+
+To constrain the manager to monitor resources within a specific namespace, set the Namespace option:
+
+```go
+mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+...
+ Cache: cache.Options{
+ DefaultNamespaces: map[string]cache.Config{"operator-namespace": cache.Config{}},
+ },
+})
+```
+
+### Watching Multiple Namespaces
+
+A manager can also be configured to watch a specified set of namespaces using [Cache Config][CacheConfig]:
+
+```go
+mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+...
+Cache: cache.Options{
+ DefaultNamespaces: map[string]cache.Config{
+ "operator-namespace1": cache.Config{},
+ "operator-namespace2": cache.Config{},
+ },
+ },
+})
+```
+
+## Configuring CRD Scope
+
+The scope of CRDs determines their visibility either within specific namespaces or across the entire cluster.
+
+### Namespace-scoped CRDs
+
+Namespace-scoped CRDs are suitable when resources need to be isolated to specific namespaces.
+This setting helps manage resources related to particular teams or applications.
+However, it is important to note that due to the unique definition of CRDs (Custom Resource Definitions) in Kubernetes, testing a new version of a CRD is not straightforward. Proper versioning and conversion strategies need to be implemented (example in our [kubebuilder tutorial][kubebuilder-multiversion-tutorial]), and coordination is required to manage which manager instance handles the conversion (see the official [kubernetes documentation][k8s-crd-conversion] about this).
+Additionally, the namespace scope must be taken into account for mutating and validating webhook configurations to ensure they are correctly applied within the intended scope. This facilitates a more controlled and phased rollout strategy.
+
+### Cluster-scoped CRDs
+
+For resources that need to be accessible and manageable across the entire cluster,
+such as shared configurations or global resources, cluster-scoped CRDs are used.
+
+#### Configuring CRDs Scopes
+
+**When the API is created**
+
+The scope of a CRD is defined when generating its manifest.
+Kubebuilder facilitates this through its API creation command.
+
+By default, APIs are created with CRD scope as namespaced. However,
+for cluster-wide you use `--namespaced=false`, i.e.:
+
+```shell
+kubebuilder create api --group cache --version v1alpha1 --kind Memcached --resource=true --controller=true --namespaced=false
+```
+
+This command generates the CRD with the Cluster scope,
+meaning it will be accessible and manageable across all
+namespaces in the cluster.
+
+**By updating existing APIs**
+
+After you create an API you are still able to change the scope.
+For example, to configure a CRD to be cluster-wide,
+add the `+kubebuilder:resource:scope=Cluster` marker
+above the API type definition in your Go file.
+Here is an example:
+
+```go
+//+kubebuilder:object:root=true
+//+kubebuilder:subresource:status
+//+kubebuilder:resource:scope=Cluster,shortName=mc
+
+...
+```
+
+After setting the desired scope with markers,
+run `make manifests` to generate the files.
+This command invokes [`controller-gen`][controller-tools] to generate the CRD manifests
+according to the markers specified in your Go files.
+
+The generated manifests will then correctly reflect
+the scope as either Cluster or Namespaced without
+needing manual adjustment in the YAML files.
+
+[controller-tools]: https://sigs.k8s.io/controller-tools
+[CacheConfig]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/cache#Config
+[kubebuilder-multiversion-tutorial]: https://book.kubebuilder.io/multiversion-tutorial/tutorial
+[k8s-crd-conversion]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion
\ No newline at end of file
diff --git a/docs/book/src/reference/strict-field-validation.md b/docs/book/src/reference/strict-field-validation.md
new file mode 100644
index 00000000000..9688485add2
--- /dev/null
+++ b/docs/book/src/reference/strict-field-validation.md
@@ -0,0 +1,214 @@
+# Strict Field Validation
+
+## Overview
+
+By default, when your controller writes an object that contains fields not defined in the CRD schema, the API server:
+
+- Accepts the request
+- Drops the unknown fields
+- May only log a warning
+
+This can hide bugs and version skew between:
+- The controller code (Go types) and
+- The CRD schema installed in the cluster
+
+`controller-runtime` exposes [client.WithFieldValidation][client-field-validation-docs] to turn on strict server-side field validation for all client writes. When enabled, the API server returns a hard error instead of silently dropping unknown fields.
+
+CRDs should be installed before controllers. However, during upgrades this can be best effort rather than guaranteed. Deployment tools may apply resources without strict ordering, and even tools with ordering features don't guarantee the CRD update succeeds. For controllers using external CRDs from third-party operators, ordering may not be controllable at all.
+
+When controllers and CRDs get out of sync, controller writes may fail with unknown field errors, status updates may not persist, and conversion webhooks can fail if the CRD schema is outdated. Controllers may crash-loop until CRDs are updated.
+
+## What does it solve
+
+Strict validation prevents silent failures when your controller code and CRD schemas get out of sync.
+
+For example, you add a new field `status.newField` to your controller, but the CRD in the cluster hasn't been updated yet. When the controller calls `client.Status().Patch(...)`:
+
+**Without strict validation:**
+- API server drops `status.newField` silently
+- Controller sees no error
+- Field never appears on the object - confusing debugging
+
+**With strict validation:**
+- API server returns clear error
+- Controller knows CRDs need updating
+- Fails fast instead of silent data loss
+
+## When to use it
+
+Strict validation is a good fit when:
+
+- You own both the CRDs and the controllers
+- Your upgrade process applies CRDs first or ensures they update together
+- You want to fail fast when a controller writes fields not in the schema
+- You want to catch bugs in your types or conversions early
+- You use typed schemas, or explicitly mark dynamic data with `x-kubernetes-preserve-unknown-fields: true`
+
+## When NOT to use it
+
+Avoid strict validation in production when:
+
+- Controllers and CRDs upgrade independently (i.e., common in Helm)
+- You manage third-party CRDs whose schemas evolve independently
+- Your CRDs use unstructured/dynamic data without `x-kubernetes-preserve-unknown-fields`
+- You need upgrade tolerance when controller and CRD versions are temporarily mismatched
+
+## How Kubebuilder scaffold handles CRD ordering
+
+### Makefile Targets (`make install` + `make deploy`)
+
+```bash
+make install # Installs CRDs into the cluster
+make deploy # Deploys the controller
+```
+
+Two separate commands. CRDs are installed first and established before the controller starts. Order is guaranteed.
+
+### YAML bundle distribution (make build-installer)
+
+```bash
+kubectl apply -f dist/install.yaml
+```
+
+The bundle positions CRDs early in the file, after Namespace and before Deployment. This works for new installations.
+
+During upgrades, if the CRD already exists and the update fails or is slow, the Deployment may still update. The new controller may start before the CRD update completes.
+
+### Helm chart distribution
+
+By using the [Helm plugin](../plugins/available/helm-v2-alpha.md), you can distribute your solution as a Helm chart package. Users can install or upgrade it with:
+
+```bash
+helm install my-operator ./dist/chart
+```
+
+Kubebuilder places CRDs in `templates/crd/` to ensure they upgrade with the controller. Helm has a built-in resource order that helps during installation.
+
+During upgrades, if the CRD update fails or is slow to propagate, Helm may still update the Deployment.
+
+Moreover, users can skip CRD updates with `helm upgrade --set crd.enable=false`.
+
+
+
+Helm recommends a `crds/` directory, but CRDs there **never upgrade**. Kubebuilder uses `templates/crd/` to keep CRDs in sync with controllers but the order is **best effort**. See [Why CRDs are added under templates](../plugins/available/helm-v2-alpha.md#why-crds-are-added-under-templates) for details.
+
+
+
+
+Upgrade and Lifecycle Considerations
+
+During upgrades, CRD updates may not complete before the controller starts. This can happen when deployment tools apply resources without explicit ordering mechanisms.
+
+CRD updates may hit errors or take time to propagate through the API server. If the controller Deployment updates at the same time, the new controller pod may start before the CRD update finishes. With strict validation enabled, controller writes will fail until the CRD is ready.
+
+Ordering tools like ArgoCD sync waves or FluxCD dependsOn can help by controlling when resources are applied. However, these control timing, not whether the CRD update succeeds. If not properly configured or if the CRD apply encounters issues, the controller may still deploy.
+
+For controllers that use CRDs from third-party operators (cert-manager, prometheus-operator, etc.), those CRDs have independent lifecycles. The third-party operator may upgrade its CRDs separately from your controller, which can lead to version mismatches.
+
+
+
+## Wiring an opt-in flag in cmd/main.go
+
+This feature is **not scaffolded by default**. Follow these steps to add it manually.
+
+### Step 1: Add the strictManager wrapper
+
+In `cmd/main.go`, add this type definition after the `init()` function:
+
+```go
+// strictManager wraps the manager to reject unknown fields instead of silently dropping them.
+// When the controller writes a field that doesn't exist in the CRD, the write fails immediately.
+// This helps catch typos and version mismatches between your code and cluster CRDs.
+type strictManager struct {
+ ctrl.Manager
+ strictClient client.Client
+}
+
+func (m *strictManager) GetClient() client.Client {
+ return m.strictClient
+}
+```
+
+### Step 2: Add required imports
+
+Add these imports to `cmd/main.go`:
+
+```go
+import (
+ // ... your existing imports ...
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+```
+
+### Step 3: Add the command-line flag
+
+In the `main()` function, where other flags are defined, add:
+
+```go
+func main() {
+ var metricsAddr string
+ var enableLeaderElection bool
+ var probeAddr string
+ var strictFieldValidation bool // Add this
+
+ flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "...")
+ flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "...")
+ flag.BoolVar(&enableLeaderElection, "leader-elect", false, "...")
+
+ // Add this flag
+ flag.BoolVar(&strictFieldValidation, "strict-field-validation", false,
+ "Reject unknown fields instead of dropping them.")
+
+ // ... rest of your code ...
+}
+```
+
+### Step 4: Wrap the manager conditionally
+
+After creating the manager with `ctrl.NewManager()`, add this wrapper logic:
+
+```go
+mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+ Scheme: scheme,
+ // ... your other options ...
+})
+if err != nil {
+ setupLog.Error(err, "unable to start manager")
+ os.Exit(1)
+}
+
+// When enabled, the controller rejects writes with unknown fields instead of silently dropping them.
+var finalMgr ctrl.Manager = mgr
+if strictFieldValidation {
+ finalMgr = &strictManager{
+ Manager: mgr,
+ strictClient: client.WithFieldValidation(
+ mgr.GetClient(),
+ metav1.FieldValidationStrict,
+ ),
+ }
+}
+
+// Use finalMgr for all subsequent setup
+if err := (&controller.MyReconciler{
+ Client: finalMgr.GetClient(),
+ Scheme: finalMgr.GetScheme(),
+}).SetupWithManager(finalMgr); err != nil {
+ setupLog.Error(err, "unable to create controller", "controller", "My")
+ os.Exit(1)
+}
+
+// Continue using finalMgr for health checks, starting manager, etc.
+if err := finalMgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+ setupLog.Error(err, "unable to set up health check")
+ os.Exit(1)
+}
+
+if err := finalMgr.Start(ctrl.SetupSignalHandler()); err != nil {
+ setupLog.Error(err, "problem running manager")
+ os.Exit(1)
+}
+```
+
+[client-field-validation-docs]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client#WithFieldValidation
diff --git a/docs/book/src/reference/submodule-layouts.md b/docs/book/src/reference/submodule-layouts.md
new file mode 100644
index 00000000000..ed67b18a862
--- /dev/null
+++ b/docs/book/src/reference/submodule-layouts.md
@@ -0,0 +1,251 @@
+# Sub-Module Layouts
+
+This part describes how to modify a scaffolded project for use with multiple `go.mod` files for APIs and Controllers.
+
+Sub-Module Layouts (in a way you could call them a special form of [Monorepo's][monorepo]) are a special use case and can help in scenarios that involve reuse of APIs without introducing indirect dependencies that should not be available in the project consuming the API externally.
+
+
+Using External Resources/APIs
+
+If you are looking to do operations and reconcile via a controller a Type(CRD) which are owned by another project
+or By Kubernetes API then, please see [Using an external Resources/API](/reference/using_an_external_type.md)
+for more info.
+
+
+
+## Overview
+
+Separate `go.mod` modules for APIs and Controllers can help for the following cases:
+
+- There is an enterprise version of an operator available that wants to reuse APIs from the Community Version
+- There are many (possibly external) modules depending on the API and you want to have a more strict separation of transitive dependencies
+- If you want to reduce impact of transitive dependencies on your API being included in other projects
+- If you are looking to separately manage the lifecycle of your API release process from your controller release process.
+- If you are looking to modularize your codebase without splitting your code between multiple repositories.
+
+They introduce however multiple caveats into typical projects which is one of the main factors that makes them hard to recommend in a generic use-case or plugin:
+
+- Multiple `go.mod` modules are not recommended as a go best practice and [multiple modules are mostly discouraged][multi-module-repositories]
+- There is always the possibility to extract your APIs into a new repository and arguably also have more control over the release process in a project spanning multiple repos relying on the same API types.
+- It requires at least one [replace directive][replace-directives] either through `go.work` which is at least 2 more files plus an environment variable for build environments without GO_WORK or through `go.mod` replace, which has to be manually dropped and added for every release.
+
+
+ Implications on Maintenance efforts
+
+When deciding to deviate from the standard kubebuilder `PROJECT` setup or the extended layouts offered by its plugins, it can result in increased maintenance overhead as there can be breaking changes in upstream that could break with the custom module structure described here.
+
+Splitting your codebase to multiple repos and/or multiple modules incurs costs that will grow over time. You'll need to define clear version dependencies between your own modules, do phased upgrades carefully, etc. Especially for small-to-medium projects, one repo and one module is the best way to go.
+
+Bear in mind, that it is not recommended to deviate from the proposed layout unless you know what you are doing.
+You may also lose the ability to use some of the CLI features and helpers. For further information on the project layout, see the doc [What's in a basic project?][basic-project-doc]
+
+
+
+## Adjusting your Project
+
+For a proper Sub-Module layout, we will use the generated APIs as a starting point.
+
+For the steps below, we will assume you created your project in your `GOPATH` with
+
+```shell
+kubebuilder init
+```
+
+and created an API & controller with
+
+```shell
+kubebuilder create api --group operator --version v1alpha1 --kind Sample --resource --controller --make
+```
+
+### Creating a second module for your API
+
+Now that we have a base layout in place, we will enable you for multiple modules.
+
+1. Navigate to `api/v1alpha1`
+2. Run `go mod init` to create a new submodule
+3. Run `go mod tidy` to resolve the dependencies
+
+Your api go.mod file could now look like this:
+
+```go.mod
+module YOUR_GO_PATH/test-operator/api/v1alpha1
+
+go 1.21.0
+
+require (
+ k8s.io/apimachinery v0.28.4
+ sigs.k8s.io/controller-runtime v0.16.3
+)
+
+require (
+ github.com/go-logr/logr v1.2.4 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/google/gofuzz v1.2.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ golang.org/x/net v0.17.0 // indirect
+ golang.org/x/text v0.13.0 // indirect
+ gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ k8s.io/klog/v2 v2.100.1 // indirect
+ k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
+ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
+)
+```
+
+As you can see it only includes apimachinery and controller-runtime as dependencies and any dependencies you have
+declared in your controller are not taken over into the indirect imports.
+
+### Using replace directives for development
+
+When trying to resolve your main module in the root folder of the operator, you will notice an error if you use a VCS path:
+
+```shell
+go mod tidy
+go: finding module for package YOUR_GO_PATH/test-operator/api/v1alpha1
+YOUR_GO_PATH/test-operator imports
+ YOUR_GO_PATH/test-operator/api/v1alpha1: cannot find module providing package YOUR_GO_PATH/test-operator/api/v1alpha1: module YOUR_GO_PATH/test-operator/api/v1alpha1: git ls-remote -q origin in LOCALVCSPATH: exit status 128:
+ remote: Repository not found.
+ fatal: repository 'https://YOUR_GO_PATH/test-operator/' not found
+```
+
+The reason for this is that you may have not pushed your modules into the VCS yet and resolving the main module will fail as it can no longer
+directly access the API types as a package but only as a module.
+
+To solve this issue, we will have to tell the go tooling to properly `replace` the API module with a local reference to your path.
+
+You can do this with 2 different approaches: go modules and go workspaces.
+
+#### Using go modules
+
+For go modules, you will edit the main `go.mod` file of your project and issue a replace directive.
+
+You can do this by editing the `go.mod` with
+``
+```shell
+go mod edit -require YOUR_GO_PATH/test-operator/api/v1alpha1@v0.0.0 # Only if you didn't already resolve the module
+go mod edit -replace YOUR_GO_PATH/test-operator/api/v1alpha1@v0.0.0=./api/v1alpha1
+go mod tidy
+```
+
+Note that we used the placeholder version `v0.0.0` of the API Module. In case you already released your API module once,
+you can use the real version as well. However this will only work if the API Module is already available in the VCS.
+
+
+ Implications on controller releases
+
+Since the main `go.mod` file now has a replace directive, it is important to drop it again before releasing your controller module.
+To achieve this you can simply run
+
+```shell
+go mod edit -dropreplace YOUR_GO_PATH/test-operator/api/v1alpha1
+go mod tidy
+```
+
+
+
+#### Using go workspaces
+
+For go workspaces, you will not edit the `go.mod` files yourself, but rely on the workspace support in go.
+
+To initialize a workspace for your project, run `go work init` in the project root.
+
+Now let us include both modules in our workspace:
+```shell
+go work use . # This includes the main module with the controller
+go work use api/v1alpha1 # This is the API submodule
+go work sync
+```
+
+This will lead to commands such as `go run` or `go build` to respect the workspace and make sure that local resolution is used.
+
+You will be able to work with this locally without having to build your module.
+
+When using `go.work` files, it is recommended to not commit them into the repository and add them to `.gitignore`.
+
+```gitignore
+go.work
+go.work.sum
+```
+
+When releasing with a present `go.work` file, make sure to set the environment variable `GOWORK=off` (verifiable with `go env GOWORK`) to make sure the release process does not get impeded by a potentially committed `go.work` file.
+
+#### Adjusting the Dockerfile
+
+When building your controller image, kubebuilder by default is not able to work with multiple modules.
+You will have to manually add the new API module into the download of dependencies:
+
+```dockerfile
+# Build the manager binary
+FROM docker.io/golang:1.20 as builder
+ARG TARGETOS
+ARG TARGETARCH
+
+WORKDIR /workspace
+# Copy the Go Modules manifests
+COPY go.mod go.mod
+COPY go.sum go.sum
+# Copy the Go Sub-Module manifests
+COPY api/v1alpha1/go.mod api/go.mod
+COPY api/v1alpha1/go.sum api/go.sum
+# cache deps before building and copying source so that we don't need to re-download as much
+# and so that source changes don't invalidate our downloaded layer
+RUN go mod download
+
+# Copy the go source
+COPY cmd/main.go cmd/main.go
+COPY api/ api/
+COPY internal/controller/ internal/controller/
+
+# Build
+# the GOARCH has not a default value to allow the binary be built according to the host where the command
+# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
+# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
+# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
+RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
+
+# Use distroless as minimal base image to package the manager binary
+# Refer to https://github.com/GoogleContainerTools/distroless for more details
+FROM gcr.io/distroless/static:nonroot
+WORKDIR /
+COPY --from=builder /workspace/manager .
+USER 65532:65532
+
+ENTRYPOINT ["/manager"]
+```
+
+### Creating a new API and controller release
+
+Because you adjusted the default layout, before releasing your first version of your operator, make sure to [familiarize yourself with mono-repo/multi-module releases][multi-module-repositories] with multiple `go.mod` files in different subdirectories.
+
+Assuming a single API was created, the release process could look like this:
+
+```sh
+git commit
+git tag v1.0.0 # this is your main module release
+git tag api/v1.0.0 # this is your api release
+go mod edit -require YOUR_GO_PATH/test-operator/api@v1.0.0 # now we depend on the api module in the main module
+go mod edit -dropreplace YOUR_GO_PATH/test-operator/api/v1alpha1 # this will drop the replace directive for local development in case you use go modules, meaning the sources from the VCS will be used instead of the ones in your monorepo checked out locally.
+git push origin main v1.0.0 api/v1.0.0
+```
+
+After this, your modules will be available in VCS and you do not need a local replacement anymore. However if you're making local changes,
+make sure to adopt your behavior with `replace` directives accordingly.
+
+### Reusing your extracted API module
+
+Whenever you want to reuse your API module with a separate kubebuilder, we will assume you follow the guide for [using an external Type](/reference/using_an_external_type.md).
+When you get to the step `Edit the API files` simply import the dependency with
+
+```shell
+go get YOUR_GO_PATH/test-operator/api@v1.0.0
+```
+
+and then use it as explained in the guide.
+
+[basic-project-doc]: ./../cronjob-tutorial/basic-project.md
+[monorepo]: https://en.wikipedia.org/wiki/Monorepo
+[replace-directives]: https://go.dev/ref/mod#go-mod-file-replace
+[multi-module-repositories]: https://github.com/golang/go/wiki/Modules#faqs--multi-module-repositories
diff --git a/docs/book/src/reference/using-finalizers.md b/docs/book/src/reference/using-finalizers.md
index 52c8f3cdbd8..ebcd464b430 100644
--- a/docs/book/src/reference/using-finalizers.md
+++ b/docs/book/src/reference/using-finalizers.md
@@ -8,7 +8,7 @@ on object's deletion from Kubernetes, you can use a finalizer to do that.
You can read more about the finalizers in the [Kubernetes reference docs](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers). The section below demonstrates how to register and trigger pre-delete hooks
in the `Reconcile` method of a controller.
-The key point to note is that a finalizer causes "delete" on the object to become
+The key point to note is that a finalizer causes "delete" on the object to become
an "update" to set deletion timestamp. Presence of deletion timestamp on the object
indicates that it is being deleted. Otherwise, without finalizers, a delete
shows up as a reconcile where the object is missing from the cache.
diff --git a/docs/book/src/reference/using_an_external_resource.md b/docs/book/src/reference/using_an_external_resource.md
new file mode 100644
index 00000000000..9234999d664
--- /dev/null
+++ b/docs/book/src/reference/using_an_external_resource.md
@@ -0,0 +1,211 @@
+# Using External Resources
+
+In some cases, your project may need to work with resources that aren't defined by your own APIs.
+These external resources fall into two main categories:
+
+- **Core Types**: API types defined by Kubernetes itself, such as `Pods`, `Services`, and `Deployments`.
+- **External Types**: API types defined in other projects, such as CRDs defined by another solution.
+
+## Managing External Types
+
+### Creating a Controller for External Types
+
+To create a controller for an external type without scaffolding a resource,
+use the `create api` command with the `--resource=false` option and specify the path to the
+external API type using the `--external-api-path` and `--external-api-domain` flag options.
+This generates a controller for types defined outside your project,
+such as CRDs managed by other Operators.
+
+The command looks like this:
+
+```shell
+kubebuilder create api --group --version --kind --controller --resource=false --external-api-path= --external-api-domain=
+```
+
+- `--external-api-path`: Provide the Go import path where the external types are defined.
+- `--external-api-domain`: Provide the domain for the external types. This value will be used to generate RBAC permissions and create the QualifiedGroup, such as - `apiGroups: .`
+
+For example, if you're managing Certificates from Cert Manager:
+
+```shell
+kubebuilder create api --group certmanager --version v1 --kind Certificate --controller=true --resource=false --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 --external-api-domain=io
+```
+
+
+Pinning External API Versions
+
+You can pin a specific version of the external API dependency using the `--external-api-module` flag:
+
+```shell
+kubebuilder create api --group certmanager --version v1 --kind Certificate \
+ --controller=true --resource=false \
+ --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 \
+ --external-api-domain=io \
+ --external-api-module=github.com/cert-manager/cert-manager@v1.18.2
+```
+
+The flag accepts the module path with optional version (e.g., `github.com/cert-manager/cert-manager@v1.18.2`).
+The module is stored in the PROJECT file and added to `go.mod` using `go get`,
+which cleanly adds it as a direct dependency without polluting go.mod with unnecessary indirect dependencies.
+
+
+
+See the RBAC [markers][markers-rbac] generated for this:
+
+```go
+// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates/finalizers,verbs=update
+```
+
+Also, the RBAC role:
+
+```ymal
+- apiGroups:
+ - cert-manager.io
+ resources:
+ - certificates
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - cert-manager.io
+ resources:
+ - certificates/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - cert-manager.io
+ resources:
+ - certificates/status
+ verbs:
+ - get
+ - patch
+ - update
+```
+
+This scaffolds a controller for the external type but skips creating new resource
+definitions since the type is defined in an external project.
+
+### Creating a Webhook to Manage an External Type
+
+You can create webhooks for external types by providing the external API path, domain, and optionally the module:
+
+```shell
+kubebuilder create webhook --group certmanager --version v1 --kind Issuer \
+ --defaulting --programmatic-validation \
+ --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 \
+ --external-api-domain=cert-manager.io
+```
+
+You can also pin the version using the `--external-api-module` flag:
+
+```shell
+kubebuilder create webhook --group certmanager --version v1 --kind Issuer \
+ --defaulting --programmatic-validation \
+ --external-api-path=github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1 \
+ --external-api-domain=cert-manager.io \
+ --external-api-module=github.com/cert-manager/cert-manager@v1.18.2
+```
+
+## Managing Core Types
+
+Core Kubernetes API types, such as `Pods`, `Services`, and `Deployments`, are predefined by Kubernetes.
+To create a controller for these core types without scaffolding the resource,
+use the Kubernetes group name described in the following
+table and specify the version and kind.
+
+| Group | K8s API Group |
+|---------------------------|------------------------------------|
+| admission | k8s.io/admission |
+| admissionregistration | k8s.io/admissionregistration |
+| apps | apps |
+| auditregistration | k8s.io/auditregistration |
+| apiextensions | k8s.io/apiextensions |
+| authentication | k8s.io/authentication |
+| authorization | k8s.io/authorization |
+| autoscaling | autoscaling |
+| batch | batch |
+| certificates | k8s.io/certificates |
+| coordination | k8s.io/coordination |
+| core | core |
+| events | k8s.io/events |
+| extensions | extensions |
+| imagepolicy | k8s.io/imagepolicy |
+| networking | k8s.io/networking |
+| node | k8s.io/node |
+| metrics | k8s.io/metrics |
+| policy | policy |
+| rbac.authorization | k8s.io/rbac.authorization |
+| scheduling | k8s.io/scheduling |
+| setting | k8s.io/setting |
+| storage | k8s.io/storage |
+
+The command to create a controller to manage `Pods` looks like this:
+
+```shell
+kubebuilder create api --group core --version v1 --kind Pod --controller=true --resource=false
+```
+
+For instance, to create a controller to manage Deployment the command would be like:
+
+```sh
+create api --group apps --version v1 --kind Deployment --controller=true --resource=false
+```
+
+See the RBAC [markers][markers-rbac] generated for this:
+
+```go
+// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=apps,resources=deployments/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=apps,resources=deployments/finalizers,verbs=update
+```
+
+Also, the RBAC for the above [markers][markers-rbac]:
+
+```yaml
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - apps
+ resources:
+ - deployments/status
+ verbs:
+ - get
+ - patch
+ - update
+```
+
+This scaffolds a controller for the Core type `corev1.Pod` but skips creating new resource
+definitions since the type is already defined in the Kubernetes API.
+
+### Creating a Webhook to Manage a Core Type
+
+You will run the command with the Core Type data, just as you would for controllers.
+See an example:
+
+```go
+kubebuilder create webhook --group core --version v1 --kind Pod --programmatic-validation
+```
+[markers-rbac]: ./markers/rbac.md
\ No newline at end of file
diff --git a/docs/book/src/reference/watching-resources.md b/docs/book/src/reference/watching-resources.md
new file mode 100644
index 00000000000..2578fff4728
--- /dev/null
+++ b/docs/book/src/reference/watching-resources.md
@@ -0,0 +1,179 @@
+# Watching Resources
+
+When extending the Kubernetes API, we aim to ensure that our solutions behave consistently with Kubernetes itself.
+For example, consider a `Deployment` resource, which is managed by a controller. This controller is responsible
+for responding to changes in the cluster—such as when a `Deployment` is created, updated, or deleted—by triggering
+reconciliation to ensure the resource’s state matches the desired state.
+
+Similarly, when developing our controllers, we want to watch for relevant changes in resources that are crucial
+to our solution. These changes—whether creations, updates, or deletions—should trigger the reconciliation
+loop to take appropriate actions and maintain consistency across the cluster.
+
+The [controller-runtime][controller-runtime] library provides several ways to watch and manage resources.
+
+## Primary Resources
+
+The **Primary Resource** is the resource that your controller is responsible
+for managing. For example, if you create a custom resource definition (CRD) for `MyApp`,
+the corresponding controller is responsible for managing instances of `MyApp`.
+
+In this case, `MyApp` is the **Primary Resource** for that controller, and your controller’s
+reconciliation loop focuses on ensuring the desired state of these primary resources is maintained.
+
+When you create a new API using Kubebuilder, the following default code is scaffolded,
+ensuring that the controller watches all relevant events—such as creations, updates, and
+deletions—for (`For()`) the new API.
+
+This setup guarantees that the reconciliation loop is triggered whenever an instance
+of the API is created, updated, or deleted:
+
+```go
+// Watches the primary resource (e.g., MyApp) for create, update, delete events
+if err := ctrl.NewControllerManagedBy(mgr).
+ For(&{}). <-- See there that the Controller is For this API
+ Complete(r); err != nil {
+ return err
+}
+```
+
+## Secondary Resources
+
+Your controller will likely also need to manage **Secondary Resources**,
+which are the resources required on the cluster to support the **Primary Resource**.
+
+Changes to these **Secondary Resources** can directly impact the **Primary Resource**,
+so the controller must watch and reconcile these resources accordingly.
+
+### Which are Owned by the Controller
+
+These **Secondary Resources**, such as `Services`, `ConfigMaps`, or `Deployments`,
+when `Owned` by the controllers, are created and managed by the specific controller
+and are tied to the **Primary Resource** via [OwnerReferences][owner-ref-k8s-docs].
+
+For example, if we have a controller to manage our CR(s) of the Kind `MyApp`
+on the cluster, which represents our application solution, all resources required
+to ensure that `MyApp` is up and running with the desired number of instances
+will be **Secondary Resources**. The code responsible for creating, deleting,
+and updating these resources will be part of the `MyApp` Controller.
+We would add the appropriate [OwnerReferences][owner-ref-k8s-docs]
+using the [controllerutil.SetControllerReference][cr-owner-ref-doc]
+function to indicate that these resources are owned by the same controller
+responsible for managing `MyApp` instances, which will be reconciled by the `MyAppReconciler`.
+
+Additionally, if the **Primary Resource** is deleted, Kubernetes' garbage collection mechanism
+ensures that all associated **Secondary Resources** are automatically deleted in a
+cascading manner.
+
+### Which are NOT `Owned` by the Controller
+
+Note that **Secondary Resources** can either be APIs/CRDs defined in your project or in other projects that are
+relevant to the **Primary Resources**, but which the specific controller is not responsible for creating or managing.
+
+For example, if we have a CRD that represents a backup solution (i.e. `MyBackup`) for our `MyApp`,
+it might need to watch changes in the `MyApp` resource to trigger reconciliation in `MyBackup`
+to ensure the desired state. Similarly, `MyApp`'s behavior might also be impacted by
+CRDs/APIs defined in other projects.
+
+In both scenarios, these resources are treated as **Secondary Resources**, even if they are not `Owned`
+(i.e., not created or managed) by the `MyAppController`.
+
+In Kubebuilder, resources that are not defined in the project itself and are not
+a **Core Type** (those not defined in the Kubernetes API) are called **External Types**.
+
+An **External Type** refers to a resource that is not defined in your
+project but one that you need to watch and respond to.
+For example, if **Operator A** manages a `MyApp` CRD for application deployment,
+and **Operator B** handles backups, **Operator B** can watch the `MyApp` CRD as an external type
+to trigger backup operations based on changes in `MyApp`.
+
+In this scenario, **Operator B** could define a `BackupConfig` CRD that relies on the state of `MyApp`.
+By treating `MyApp` as a **Secondary Resource**, **Operator B** can watch and reconcile changes in **Operator A**'s `MyApp`,
+ensuring that backup processes are initiated whenever `MyApp` is updated or scaled.
+
+## General Concept of Watching Resources
+
+Whether a resource is defined within your project or comes from an external project, the concept of **Primary**
+and **Secondary Resources** remains the same:
+- The **Primary Resource** is the resource the controller is primarily responsible for managing.
+- **Secondary Resources** are those that are required to ensure the primary resource works as desired.
+
+Therefore, regardless of whether the resource was defined by your project or by another project,
+your controller can watch, reconcile, and manage changes to these resources as needed.
+
+## Why does watching the secondary resources matter?
+
+When building a Kubernetes controller, it’s crucial to not only focus
+on **Primary Resources** but also to monitor **Secondary Resources**.
+Failing to track these resources can lead to inconsistencies in your
+controller's behavior and the overall cluster state.
+
+Secondary resources may not be directly managed by your controller,
+but changes to these resources can still significantly
+impact the primary resource and your controller's functionality.
+Here are the key reasons why it's important to watch them:
+
+- **Ensuring Consistency**:
+ - Secondary resources (e.g., child objects or external dependencies) may diverge from their desired state.
+ For instance, a secondary resource may be modified or deleted, causing the system to fall out of sync.
+ - Watching secondary resources ensures that any changes are detected immediately, allowing the controller to
+ reconcile and restore the desired state.
+
+- **Avoiding Random Self-Healing**:
+ - Without watching secondary resources, the controller may "heal" itself only upon restart or when specific events
+ are triggered. This can cause unpredictable or delayed reactions to issues.
+ - Monitoring secondary resources ensures that inconsistencies are addressed promptly, rather than waiting for a
+ controller restart or external event to trigger reconciliation.
+
+- **Effective Lifecycle Management**:
+ - Secondary resources might not be owned by the controller directly, but their state still impacts the behavior
+ of primary resources. Without watching these, you risk leaving orphaned or outdated resources.
+ - Watching non-owned secondary resources lets the controller respond to lifecycle events (create, update, delete)
+ that might affect the primary resource, ensuring consistent behavior across the system.
+
+See [Watching Secondary Resources That Are Not Owned](./watching-resources/secondary-resources-not-owned.md#configuration-example) for an example.
+
+## Why not use `RequeueAfter X` for all scenarios instead of watching resources?
+
+Kubernetes controllers are fundamentally **event-driven**. When creating a controller,
+the **Reconciliation Loop** is typically triggered by **events** such as `create`, `update`, or
+`delete` actions on resources. This event-driven approach is more efficient and responsive
+compared to constantly requeuing or polling resources using `RequeueAfter`. This ensures that
+the system only takes action when necessary, maintaining both performance and efficiency.
+
+In many cases, **watching resources** is the preferred approach for ensuring Kubernetes resources
+remain in the desired state. It is more efficient, responsive, and aligns with Kubernetes' event-driven architecture.
+However, there are scenarios where `RequeueAfter` is appropriate and necessary, particularly for managing external
+systems that do not emit events or for handling resources that take time to converge, such as long-running processes.
+Relying solely on `RequeueAfter` for all scenarios can lead to unnecessary overhead and
+delayed reactions. Therefore, it is essential to prioritize **event-driven reconciliation** by configuring
+your controller to **watch resources** whenever possible, and reserving `RequeueAfter` for situations
+where periodic checks are required.
+
+### When `RequeueAfter X` is Useful
+
+While `RequeueAfter` is not the primary method for triggering reconciliations, there are specific cases where it is
+necessary, such as:
+
+- **Observing External Systems**: When working with external resources that do not generate events
+(e.g., external databases or third-party services), `RequeueAfter` allows the
+controller to periodically check the status of these resources.
+- **Time-Based Operations**: Some tasks, such as rotating secrets or
+renewing certificates, must happen at specific intervals. `RequeueAfter` ensures these operations
+are performed on schedule, even when no other changes occur.
+- **Handling Errors or Delays**: When managing resources that encounter errors or require time to self-heal,
+`RequeueAfter` ensures the controller waits for a specified duration before checking the resource’s status again,
+avoiding constant reconciliation attempts.
+
+## Usage of Predicates
+
+For more complex use cases, [Predicates][cr-predicates] can be used to fine-tune
+when your controller should trigger reconciliation. Predicates allow you to filter
+events based on specific conditions, such as changes to particular fields, labels, or annotations,
+ensuring that your controller only responds to relevant events and operates efficiently.
+
+[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
+[owner-ref-k8s-docs]: https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/
+[cr-predicates]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/predicate
+[secondary-resources-doc]: watching-resources/secondary-owned-resources
+[predicates-with-external-type-doc]: watching-resources/predicates-with-watch
+[cr-owner-ref-doc]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil#SetOwnerReference
diff --git a/docs/book/src/reference/watching-resources/predicates-with-watch.md b/docs/book/src/reference/watching-resources/predicates-with-watch.md
new file mode 100644
index 00000000000..777e1369b34
--- /dev/null
+++ b/docs/book/src/reference/watching-resources/predicates-with-watch.md
@@ -0,0 +1,113 @@
+# Using Predicates to Refine Watches
+
+When working with controllers, it's often beneficial to use **Predicates** to
+filter events and control when the reconciliation loop should be triggered.
+
+[Predicates][predicates-doc] allow you to define conditions based on events (such as create, update, or delete)
+and resource fields (such as labels, annotations, or status fields). By using **[Predicates][predicates-doc]**,
+you can refine your controller’s behavior to respond only to specific changes in the resources
+it watches.
+
+This can be especially useful when you want to refine which
+changes in resources should trigger a reconciliation. By using predicates,
+you avoid unnecessary reconciliations and can ensure that the
+controller only reacts to relevant changes.
+
+## When to Use Predicates
+
+**Predicates are useful when:**
+
+- You want to ignore certain changes, such as updates that don't impact the fields your controller is concerned with.
+- You want to trigger reconciliation only for resources with specific labels or annotations.
+- You want to watch external resources and react only to specific changes.
+
+## Example: Using Predicates to Filter Update Events
+
+Let’s say that we only want our **`BackupBusybox`** controller to reconcile
+when certain fields of the **`Busybox`** resource change, for example, when
+the `spec.size` field changes, but we want to ignore all other changes (such as status updates).
+
+### Defining a Predicate
+
+In the following example, we define a predicate that only
+allows reconciliation when there’s a meaningful update
+to the **`Busybox`** resource:
+
+```go
+import (
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+ "sigs.k8s.io/controller-runtime/pkg/event"
+)
+
+// Predicate to trigger reconciliation only on size changes in the Busybox spec
+updatePred := predicate.Funcs{
+ // Only allow updates when the spec.size of the Busybox resource changes
+ UpdateFunc: func(e event.UpdateEvent) bool {
+ oldObj := e.ObjectOld.(*examplecomv1alpha1.Busybox)
+ newObj := e.ObjectNew.(*examplecomv1alpha1.Busybox)
+
+ // Trigger reconciliation only if the spec.size field has changed
+ return oldObj.Spec.Size != newObj.Spec.Size
+ },
+
+ // Allow create events
+ CreateFunc: func(e event.CreateEvent) bool {
+ return true
+ },
+
+ // Allow delete events
+ DeleteFunc: func(e event.DeleteEvent) bool {
+ return true
+ },
+
+ // Allow generic events (e.g., external triggers)
+ GenericFunc: func(e event.GenericEvent) bool {
+ return true
+ },
+}
+```
+
+### Explanation
+
+In this example:
+- The **`UpdateFunc`** returns `true` only if the **`spec.size`** field has changed between the old and new objects, meaning that all other changes in the `spec`, like annotations or other fields, will be ignored.
+- **`CreateFunc`**, **`DeleteFunc`**, and **`GenericFunc`** return `true`, meaning that create, delete, and generic events are still processed, allowing reconciliation to happen for these event types.
+
+This ensures that the controller reconciles only when the specific field **`spec.size`** is modified, while ignoring any other modifications in the `spec` that are irrelevant to your logic.
+
+### Example: Using Predicates in `Watches`
+
+Now, we apply this predicate in the **`Watches()`** method of
+the **`BackupBusyboxReconciler`** to trigger reconciliation only for relevant events:
+
+```go
+// SetupWithManager sets up the controller with the Manager.
+// The controller will watch both the BackupBusybox primary resource and the Busybox resource, using predicates.
+func (r *BackupBusyboxReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&examplecomv1alpha1.BackupBusybox{}). // Watch the primary resource (BackupBusybox)
+ Watches(
+ &examplecomv1alpha1.Busybox{}, // Watch the Busybox CR
+ handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
+ return []reconcile.Request{
+ {
+ NamespacedName: types.NamespacedName{
+ Name: "backupbusybox", // Reconcile the associated BackupBusybox resource
+ Namespace: obj.GetNamespace(), // Use the namespace of the changed Busybox
+ },
+ },
+ }
+ }),
+ builder.WithPredicates(updatePred), // Apply the predicate
+ ). // Trigger reconciliation when the Busybox resource changes (if it meets predicate conditions)
+ Complete(r)
+}
+```
+
+### Explanation
+
+- **[`builder.WithPredicates(updatePred)`][predicates-doc]**: This method applies the predicate, ensuring that reconciliation only occurs
+when the **`spec.size`** field in **`Busybox`** changes.
+- **Other Events**: The controller will still trigger reconciliation on `Create`, `Delete`, and `Generic` events.
+
+[predicates-doc]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/source#WithPredicates
\ No newline at end of file
diff --git a/docs/book/src/reference/watching-resources/secondary-owned-resources.md b/docs/book/src/reference/watching-resources/secondary-owned-resources.md
new file mode 100644
index 00000000000..0ac35fed018
--- /dev/null
+++ b/docs/book/src/reference/watching-resources/secondary-owned-resources.md
@@ -0,0 +1,188 @@
+# Watching Secondary Resources `Owned` by the Controller
+
+In Kubernetes controllers, it’s common to manage both **Primary Resources**
+and **Secondary Resources**. A **Primary Resource** is the main resource
+that the controller is responsible for, while **Secondary Resources**
+are created and managed by the controller to support the **Primary Resource**.
+
+In this section, we will explain how to manage **Secondary Resources**
+which are `Owned` by the controller. This example shows how to:
+
+- Set the [Owner Reference][cr-owner-ref-doc] between the primary resource (`Busybox`) and the secondary resource (`Deployment`) to ensure proper lifecycle management.
+- Configure the controller to `Watch` the secondary resource using `Owns()` in `SetupWithManager()`. See that `Deployment` is owned by the `Busybox` controller because
+it will be created and managed by it.
+
+## Setting the Owner Reference
+
+To link the lifecycle of the secondary resource (`Deployment`)
+to the primary resource (`Busybox`), we need to set
+an [Owner Reference][cr-owner-ref-doc] on the secondary resource.
+This ensures that Kubernetes automatically handles cascading deletions:
+if the primary resource is deleted, the secondary resource will also be deleted.
+
+Controller-runtime provides the [controllerutil.SetControllerReference][cr-owner-ref-doc] function, which you can use to set this relationship between the resources.
+
+### Setting the Owner Reference
+
+Below, we create the `Deployment` and set the Owner reference between the `Busybox` custom resource and the `Deployment` using `controllerutil.SetControllerReference()`.
+
+```go
+// deploymentForBusybox returns a Deployment object for Busybox
+func (r *BusyboxReconciler) deploymentForBusybox(busybox *examplecomv1alpha1.Busybox) *appsv1.Deployment {
+ replicas := busybox.Spec.Size
+
+ dep := &appsv1.Deployment{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: busybox.Name,
+ Namespace: busybox.Namespace,
+ },
+ Spec: appsv1.DeploymentSpec{
+ Replicas: &replicas,
+ Selector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"app": busybox.Name},
+ },
+ Template: metav1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: map[string]string{"app": busybox.Name},
+ },
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{
+ {
+ Name: "busybox",
+ Image: "busybox:latest",
+ },
+ },
+ },
+ },
+ },
+ }
+
+ // Set the ownerRef for the Deployment, ensuring that the Deployment
+ // will be deleted when the Busybox CR is deleted.
+ controllerutil.SetControllerReference(busybox, dep, r.Scheme)
+ return dep
+}
+```
+
+### Explanation
+
+By setting the `OwnerReference`, if the `Busybox` resource is deleted, Kubernetes will automatically delete
+the `Deployment` as well. This also allows the controller to watch for changes in the `Deployment`
+and ensure that the desired state (such as the number of replicas) is maintained.
+
+For example, if someone modifies the `Deployment` to change the replica count to 3,
+while the `Busybox` CR defines the desired state as 1 replica,
+the controller will reconcile this and ensure the `Deployment`
+is scaled back to 1 replica.
+
+**Reconcile Function Example**
+
+```go
+// Reconcile handles the main reconciliation loop for Busybox and the Deployment
+func (r *BusyboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+ log := logf.FromContext(ctx)
+
+ // Fetch the Busybox instance
+ busybox := &examplecomv1alpha1.Busybox{}
+ if err := r.Get(ctx, req.NamespacedName, busybox); err != nil {
+ if apierrors.IsNotFound(err) {
+ log.Info("Busybox resource not found. Ignoring since it must be deleted")
+ return ctrl.Result{}, nil
+ }
+ log.Error(err, "Failed to get Busybox")
+ return ctrl.Result{}, err
+ }
+
+ // Check if the Deployment already exists, if not create a new one
+ found := &appsv1.Deployment{}
+ err := r.Get(ctx, types.NamespacedName{Name: busybox.Name, Namespace: busybox.Namespace}, found)
+ if err != nil && apierrors.IsNotFound(err) {
+ // Define a new Deployment
+ dep := r.deploymentForBusybox(busybox)
+ log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
+ if err := r.Create(ctx, dep); err != nil {
+ log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
+ return ctrl.Result{}, err
+ }
+ // Requeue the request to ensure the Deployment is created
+ return ctrl.Result{RequeueAfter: time.Minute}, nil
+ } else if err != nil {
+ log.Error(err, "Failed to get Deployment")
+ return ctrl.Result{}, err
+ }
+
+ // Ensure the Deployment size matches the desired state
+ size := busybox.Spec.Size
+ if *found.Spec.Replicas != size {
+ found.Spec.Replicas = &size
+ if err := r.Update(ctx, found); err != nil {
+ log.Error(err, "Failed to update Deployment size", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
+ return ctrl.Result{}, err
+ }
+ // Requeue the request to ensure the correct state is achieved
+ return ctrl.Result{Requeue: true}, nil
+ }
+
+ // Update Busybox status to reflect that the Deployment is available
+ busybox.Status.AvailableReplicas = found.Status.AvailableReplicas
+ if err := r.Status().Update(ctx, busybox); err != nil {
+ log.Error(err, "Failed to update Busybox status")
+ return ctrl.Result{}, err
+ }
+
+ return ctrl.Result{}, nil
+}
+```
+
+## Watching Secondary Resources
+
+To ensure that changes to the secondary resource (such as the `Deployment`) trigger
+a reconciliation of the primary resource (`Busybox`), we configure the controller
+to watch both resources.
+
+The `Owns()` method allows you to specify secondary resources
+that the controller should monitor. This way, the controller will
+automatically reconcile the primary resource whenever the secondary
+resource changes (e.g., is updated or deleted).
+
+### Example: Configuring `SetupWithManager` to Watch Secondary Resources
+
+```go
+// SetupWithManager sets up the controller with the Manager.
+// The controller will watch both the Busybox primary resource and the Deployment secondary resource.
+func (r *BusyboxReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&examplecomv1alpha1.Busybox{}). // Watch the primary resource
+ Owns(&appsv1.Deployment{}). // Watch the secondary resource (Deployment)
+ Complete(r)
+}
+```
+
+## Ensuring the Right Permissions
+
+Kubebuilder uses [markers][markers] to define RBAC permissions
+required by the controller. In order for the controller to
+properly watch and manage both the primary (`Busybox`) and secondary (`Deployment`)
+resources, it must have the appropriate permissions granted;
+i.e. to `watch`, `get`, `list`, `create`, `update`, and `delete` permissions for those resources.
+
+### Example: RBAC Markers
+
+Before the `Reconcile` method, we need to define the appropriate RBAC markers.
+These markers will be used by [controller-gen][controller-gen] to generate the necessary
+roles and permissions when you run `make manifests`.
+
+```go
+// +kubebuilder:rbac:groups=example.com,resources=busyboxes,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
+```
+
+- The first marker gives the controller permission to manage the `Busybox` custom resource (the primary resource).
+- The second marker grants the controller permission to manage `Deployment` resources (the secondary resource).
+
+Note that we are granting permissions to `watch` the resources.
+
+[owner-ref-k8s-docs]: https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/
+[cr-owner-ref-doc]: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil#SetOwnerReference
+[controller-gen]: ./../controller-gen.md
+[markers]:./../markers/rbac.md
diff --git a/docs/book/src/reference/watching-resources/secondary-resources-not-owned.md b/docs/book/src/reference/watching-resources/secondary-resources-not-owned.md
new file mode 100644
index 00000000000..619b925054e
--- /dev/null
+++ b/docs/book/src/reference/watching-resources/secondary-resources-not-owned.md
@@ -0,0 +1,89 @@
+# Watching Secondary Resources that are NOT `Owned`
+
+In some scenarios, a controller may need to watch and respond to changes in
+resources that it does not `Own`, meaning those resources are created and managed by
+another controller.
+
+The following examples demonstrate how a controller can monitor and reconcile resources
+that it doesn’t directly manage. This applies to any resource not `Owned` by the controller,
+including **Core Types** or **Custom Resources** managed by other controllers or projects
+and reconciled in separate processes.
+
+For instance, consider two custom resources—`Busybox` and `BackupBusybox`.
+If changes to `Busybox` should trigger reconciliation in the `BackupBusybox` controller, we
+can configure the `BackupBusybox` controller to watch for updates in `Busybox`.
+
+### Example: Watching a Non-Owned Busybox Resource to Reconcile BackupBusybox
+
+Consider a controller that manages a custom resource `BackupBusybox`
+but also needs to monitor changes to `Busybox` resources across the cluster.
+We only want to trigger reconciliation when `Busybox` instances have the Backup
+feature enabled.
+
+- **Why Watch Secondary Resources?**
+ - The `BackupBusybox` controller is not responsible for creating or owning `Busybox`
+ resources, but changes in these resources (such as updates or deletions) directly affect the primary
+ resource (`BackupBusybox`).
+ - By watching `Busybox` instances with a specific label, the controller ensures that the necessary
+ actions (e.g., backups) are triggered only for the relevant resources.
+
+### Configuration Example
+
+Here’s how to configure the `BackupBusyboxReconciler` to watch changes in the
+`Busybox` resource and trigger reconciliation for `BackupBusybox`:
+
+```go
+// SetupWithManager sets up the controller with the Manager.
+// The controller will watch both the BackupBusybox primary resource and the Busybox resource.
+func (r *BackupBusyboxReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&examplecomv1alpha1.BackupBusybox{}). // Watch the primary resource (BackupBusybox)
+ Watches(
+ &examplecomv1alpha1.Busybox{}, // Watch the Busybox CR
+ handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
+ // Trigger reconciliation for the BackupBusybox in the same namespace
+ return []reconcile.Request{
+ {
+ NamespacedName: types.NamespacedName{
+ Name: "backupbusybox", // Reconcile the associated BackupBusybox resource
+ Namespace: obj.GetNamespace(), // Use the namespace of the changed Busybox
+ },
+ },
+ }
+ }),
+ ). // Trigger reconciliation when the Busybox resource changes
+ Complete(r)
+}
+```
+
+Here’s how we can configure the controller to filter and watch
+for changes to only those `Busybox` resources that have the specific label:
+
+```go
+// SetupWithManager sets up the controller with the Manager.
+// The controller will watch both the BackupBusybox primary resource and the Busybox resource, filtering by a label.
+func (r *BackupBusyboxReconciler) SetupWithManager(mgr ctrl.Manager) error {
+ return ctrl.NewControllerManagedBy(mgr).
+ For(&examplecomv1alpha1.BackupBusybox{}). // Watch the primary resource (BackupBusybox)
+ Watches(
+ &examplecomv1alpha1.Busybox{}, // Watch the Busybox CR
+ handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
+ // Check if the Busybox resource has the label 'backup-needed: "true"'
+ if val, ok := obj.GetLabels()["backup-enable"]; ok && val == "true" {
+ // If the label is present and set to "true", trigger reconciliation for BackupBusybox
+ return []reconcile.Request{
+ {
+ NamespacedName: types.NamespacedName{
+ Name: "backupbusybox", // Reconcile the associated BackupBusybox resource
+ Namespace: obj.GetNamespace(), // Use the namespace of the changed Busybox
+ },
+ },
+ }
+ }
+ // If the label is not present or doesn't match, don't trigger reconciliation
+ return []reconcile.Request{}
+ }),
+ ). // Trigger reconciliation when the labeled Busybox resource changes
+ Complete(r)
+}
+```
diff --git a/docs/book/src/reference/webhook-for-core-types.md b/docs/book/src/reference/webhook-for-core-types.md
deleted file mode 100644
index a27cc1f7e2e..00000000000
--- a/docs/book/src/reference/webhook-for-core-types.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# Admission Webhook for Core Types
-
-It is very easy to build admission webhooks for CRDs, which has been covered in
-the CronJob tutorial. Given that kubebuilder doesn't support webhook scaffolding
-for core types, you have to use the library from controller-runtime to handle it.
-There is an [example](https://github.com/kubernetes-sigs/controller-runtime/tree/master/examples/builtins)
-in controller-runtime.
-
-It is suggested to use kubebuilder to initialize a project, and then you can
-follow the steps below to add admission webhooks for core types.
-
-## Implement Your Handler
-
-You need to have your handler implements the
-[admission.Handler](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/webhook/admission?tab=doc#Handler)
-interface.
-
-```go
-type podAnnotator struct {
- Client client.Client
- decoder *admission.Decoder
-}
-
-func (a *podAnnotator) Handle(ctx context.Context, req admission.Request) admission.Response {
- pod := &corev1.Pod{}
- err := a.decoder.Decode(req, pod)
- if err != nil {
- return admission.Errored(http.StatusBadRequest, err)
- }
-
- // mutate the fields in pod
-
- marshaledPod, err := json.Marshal(pod)
- if err != nil {
- return admission.Errored(http.StatusInternalServerError, err)
- }
- return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
-}
-```
-
-If you need a client, just pass in the client at struct construction time.
-
-If you add the `InjectDecoder` method for your handler, a decoder will be
-injected for you.
-
-```go
-func (a *podAnnotator) InjectDecoder(d *admission.Decoder) error {
- a.decoder = d
- return nil
-}
-```
-
-**Note**: in order to have controller-gen generate the webhook configuration for
-you, you need to add markers. For example,
-`// +kubebuilder:webhook:path=/mutate-v1-pod,mutating=true,failurePolicy=fail,groups="",resources=pods,verbs=create;update,versions=v1,name=mpod.kb.io`
-
-## Update main.go
-
-Now you need to register your handler in the webhook server.
-
-```go
-mgr.GetWebhookServer().Register("/mutate-v1-pod", &webhook.Admission{Handler: &podAnnotator{Client: mgr.GetClient()}})
-```
-
-You need to ensure the path here match the path in the marker.
-
-## Deploy
-
-Deploying it is just like deploying a webhook server for CRD. You need to
-1) provision the serving certificate
-2) deploy the server
-
-You can follow the [tutorial](/cronjob-tutorial/running.md).
diff --git a/docs/book/src/reference/webhook-overview.md b/docs/book/src/reference/webhook-overview.md
index 533636444eb..0925cfb594e 100644
--- a/docs/book/src/reference/webhook-overview.md
+++ b/docs/book/src/reference/webhook-overview.md
@@ -1,8 +1,8 @@
# Webhook
Webhooks are requests for information sent in a blocking fashion. A web
-application implementing webhooks will send an HTTP request to other application
-when certain event happens.
+application implementing webhooks will send a HTTP request to other applications
+when a certain event happens.
In the kubernetes world, there are 3 kinds of webhooks:
[admission webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#admission-webhooks),
@@ -18,18 +18,3 @@ feature entered beta).
Kubernetes supports the conversion webhooks as of version 1.15 (when the
feature entered beta).
-### Supporting older cluster versions
-
-By default, `kubebuilder create webhook` will create webhook configs of API version `v1`,
-a version introduced in Kubernetes v1.16. If your project intends to support
-Kubernetes cluster versions older than v1.16, you must use the `v1beta1` API version:
-
-```sh
-kubebuilder create webhook --webhook-version v1beta1 ...
-```
-
-
-
-`v1beta1` is deprecated and will be removed in a future Kubernetes release, so upgrading is recommended.
-
-
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/Makefile b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/Makefile
new file mode 100644
index 00000000000..f044d2df463
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/Makefile
@@ -0,0 +1,65 @@
+
+# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
+ifeq (,$(shell go env GOBIN))
+GOBIN=$(shell go env GOPATH)/bin
+else
+GOBIN=$(shell go env GOBIN)
+endif
+
+# Setting SHELL to bash allows bash commands to be executed by recipes.
+# Options are set to exit when a recipe line exits non-zero or a piped command fails.
+SHELL = /usr/bin/env bash -o pipefail
+
+.SHELLFLAGS = -ec
+
+.PHONY: all
+all: build
+
+
+##@ General
+
+# The help target prints out all targets with their descriptions organized
+# beneath their categories. The categories are represented by '##@' and the
+# target descriptions by '##'. The awk command is responsible for reading the
+# entire set of makefiles included in this invocation, looking for lines of the
+# file as xyz: ## something, and then pretty-format the target and help. Then,
+# if there's a line with ##@ something, that gets pretty-printed as a category.
+# More info on the usage of ANSI control characters for terminal formatting:
+# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+# More info on the awk command:
+# http://linuxcommand.org/lc3_adv_awk.php
+
+.PHONY: help
+help: ## Display this help.
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+##@ Development
+
+.PHONY: tidy
+tidy: ## Run go mod tidy against code.
+ go mod tidy
+
+.PHONY: fmt
+fmt: ## Run go fmt against code.
+ go fmt ./...
+
+.PHONY: vet
+vet: ## Run go vet against code.
+ go vet ./...
+
+##@ Build
+
+.PHONY: build
+build: tidy fmt vet ## Build manager binary.
+ go build -o ./bin/sampleexternalplugin
+
+.PHONY: install
+install: build ## Build and install the binary with the current source code. Use it to test your changes locally.
+ rm -f $(GOBIN)/sampleexternalplugin
+ cp ./bin/sampleexternalplugin $(GOBIN)/sampleexternalplugin
+ # Make the binary discoverable for kubebuilder
+ ./install.sh
+
+.PHONY: test-plugin
+test-plugin: ## Run the plugin test.
+ ./test/test.sh
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/cmd.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/cmd.go
new file mode 100644
index 00000000000..f64d0e43a66
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/cmd.go
@@ -0,0 +1,108 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package cmd
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+
+ "v1/scaffolds"
+
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+// Run will run the actual steps of the plugin
+func Run() {
+ // Phase 2 Plugins makes requests to an external plugin by
+ // writing to the STDIN buffer. This means that an external plugin
+ // call will NOT include any arguments other than the program name
+ // itself. In order to get the request JSON from Kubebuilder
+ // we will need to read the input from STDIN
+ reader := bufio.NewReader(os.Stdin)
+
+ input, err := io.ReadAll(reader)
+ if err != nil {
+ returnError(fmt.Errorf("encountered error reading STDIN: %w", err))
+ }
+
+ // Parse the JSON input from STDIN to a PluginRequest object.
+ // Since the Phase 2 Plugin implementation was written in Go
+ // there is already a Go API in place to represent these values.
+ // Phase 2 Plugins can be written in any language, but you may
+ // need to create some classes/interfaces to parse the JSON used
+ // in the Phase 2 Plugins communication. More information on the
+ // Phase 2 Plugin JSON schema can be found in the Phase 2 Plugins docs
+ pluginRequest := &external.PluginRequest{}
+
+ err = json.Unmarshal(input, pluginRequest)
+ if err != nil {
+ returnError(fmt.Errorf("encountered error unmarshaling STDIN: %w", err))
+ }
+
+ var response external.PluginResponse
+
+ // Run logic depending on the command that is requested by Kubebuilder
+ switch pluginRequest.Command {
+ // the `init` subcommand is often used when initializing a new project
+ case "init":
+ response = scaffolds.InitCmd(pluginRequest)
+ // the `create api` subcommand is often used after initializing a project
+ // with the `init` subcommand to create a controller and CRDs for a
+ // provided group, version, and kind
+ case "create api":
+ response = scaffolds.ApiCmd(pluginRequest)
+ // the `create webhook` subcommand is often used after initializing a project
+ // with the `init` subcommand to create a webhook for a provided
+ // group, version, and kind
+ case "create webhook":
+ response = scaffolds.WebhookCmd(pluginRequest)
+ // the `flags` subcommand is used to customize the flags that
+ // the Kubebuilder cli will bind for use with this plugin
+ case "flags":
+ response = flagsCmd(pluginRequest)
+ // the `metadata` subcommand is used to customize the
+ // plugin metadata (help message and examples) that are
+ // shown to Kubebuilder CLI users.
+ case "metadata":
+ response = metadataCmd(pluginRequest)
+ // Any errors should still be returned as part of the plugin's
+ // JSON response. There is an `error` boolean field to signal to
+ // Kubebuilder that the external plugin encountered an error.
+ // There is also an `errorMsgs` string array field to provide all
+ // error messages to Kubebuilder.
+ default:
+ response = external.PluginResponse{
+ Error: true,
+ ErrorMsgs: []string{
+ "unknown subcommand:" + pluginRequest.Command,
+ },
+ }
+ }
+
+ // The Phase 2 Plugins implementation will read the response
+ // from a Phase 2 Plugin via STDOUT. For Kubebuilder to properly
+ // read our response we need to create a valid JSON string and
+ // write it to the STDOUT buffer.
+ output, err := json.Marshal(response)
+ if err != nil {
+ returnError(fmt.Errorf("encountered error marshaling output: %w | OUTPUT: %s", err, output))
+ }
+
+ fmt.Printf("%s", output)
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/flags.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/flags.go
new file mode 100644
index 00000000000..d259926bbcc
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/flags.go
@@ -0,0 +1,54 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package cmd
+
+import (
+ "v1/scaffolds"
+
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+// flagsCmd handles all the logic for the `flags` subcommand of the sample external plugin.
+// In Kubebuilder's Phase 2 Plugins the `flags` subcommand is an optional subcommand for
+// external plugins to support. The `flags` subcommand allows for an external plugin
+// to provide Kubebuilder with a list of flags that the `init`, `create api`, `create webhook`,
+// and `edit` subcommands allow. This allows Kubebuilder to give an external plugin the ability
+// to feel like a native Kubebuilder plugin to a Kubebuilder user by only binding the supported
+// flags and failing early if an unknown flag is provided.
+func flagsCmd(pr *external.PluginRequest) external.PluginResponse {
+ pluginResponse := external.PluginResponse{
+ APIVersion: "v1alpha1",
+ Command: "flags",
+ Universe: pr.Universe,
+ Flags: []external.Flag{},
+ }
+
+ switch pr.Command {
+ case "init":
+ pluginResponse.Flags = scaffolds.InitFlags
+ case "create api":
+ pluginResponse.Flags = scaffolds.ApiFlags
+ case "create webhook":
+ pluginResponse.Flags = scaffolds.WebhookFlags
+ default:
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ "unrecognized command: " + pr.Command,
+ }
+ }
+
+ return pluginResponse
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/helpers.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/helpers.go
new file mode 100644
index 00000000000..4ea33ad4830
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/helpers.go
@@ -0,0 +1,50 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package cmd
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+// returnError is a helper function to return a JSON
+// response that states an error has occurred along
+// with the error message to Kubebuilder. If this
+// function encounters an error printing the JSON
+// response it just prints a normal error message
+// and exits with a non-zero exit code. Kubebuilder
+// will detect that an error has occurred if there is
+// a non-zero exit code from the external plugin, but
+// it is recommended to return a JSON response that states
+// an error has occurred to provide the best user experience
+// and integration with Kubebuilder.
+func returnError(err error) {
+ errResponse := external.PluginResponse{
+ Error: true,
+ ErrorMsgs: []string{
+ err.Error(),
+ },
+ }
+ output, err := json.Marshal(errResponse)
+ if err != nil {
+ log.Fatalf("encountered error marshaling output: %s | OUTPUT: %s", err.Error(), output)
+ }
+
+ fmt.Printf("%s", output)
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/metadata.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/metadata.go
new file mode 100644
index 00000000000..cd31799a51d
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/cmd/metadata.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package cmd
+
+import (
+ "fmt"
+
+ "v1/scaffolds"
+
+ "github.com/spf13/pflag"
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+// metadataCmd handles all the logic for the `metadata` subcommand of the sample external plugin.
+// In Kubebuilder's Phase 2 Plugins the `metadata` subcommand is an optional subcommand for
+// external plugins to support. The `metadata` subcommand allows for an external plugin
+// to provide Kubebuilder with a description of the plugin and examples for each of the
+// `init`, `create api`, `create webhook`, and `edit` subcommands. This allows Kubebuilder
+// to provide users a native Kubebuilder plugin look and feel for an external plugin.
+func metadataCmd(pr *external.PluginRequest) external.PluginResponse {
+ pluginResponse := external.PluginResponse{
+ APIVersion: "v1alpha1",
+ Command: "flags",
+ Universe: pr.Universe,
+ }
+
+ // Here is an example of parsing multiple flags from a Kubebuilder external plugin request
+ flagsToParse := pflag.NewFlagSet("flagsFlags", pflag.ContinueOnError)
+ flagsToParse.Bool("init", false, "sets the init flag to true")
+ flagsToParse.Bool("api", false, "sets the api flag to true")
+ flagsToParse.Bool("webhook", false, "sets the webhook flag to true")
+
+ if err := flagsToParse.Parse(pr.Args); err != nil {
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ fmt.Sprintf("failed to parse flags: %s", err.Error()),
+ }
+ return pluginResponse
+ }
+
+ initFlag, _ := flagsToParse.GetBool("init")
+ apiFlag, _ := flagsToParse.GetBool("api")
+ webhookFlag, _ := flagsToParse.GetBool("webhook")
+
+ // The Phase 2 Plugins implementation will only ever pass a single boolean flag
+ // argument in the JSON request `args` field. The flag will be `--init` if it is
+ // attempting to get the flags for the `init` subcommand, `--api` for `create api`,
+ // `--webhook` for `create webhook`, and `--edit` for `edit`
+ if initFlag {
+ // Populate the JSON response `metadata` field with a description
+ // and examples for the `init` subcommand
+ pluginResponse.Metadata = scaffolds.InitMeta
+ } else if apiFlag {
+ pluginResponse.Metadata = scaffolds.ApiMeta
+ } else if webhookFlag {
+ pluginResponse.Metadata = scaffolds.WebhookMeta
+ } else {
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ "unrecognized flag",
+ }
+ }
+
+ return pluginResponse
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.mod b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.mod
new file mode 100644
index 00000000000..d790c2162b3
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.mod
@@ -0,0 +1,17 @@
+module v1
+
+go 1.24.6
+
+require (
+ github.com/spf13/pflag v1.0.10
+ sigs.k8s.io/kubebuilder/v4 v4.9.0
+)
+
+require (
+ github.com/gobuffalo/flect v1.0.3 // indirect
+ github.com/spf13/afero v1.15.0 // indirect
+ golang.org/x/mod v0.28.0 // indirect
+ golang.org/x/sync v0.17.0 // indirect
+ golang.org/x/text v0.29.0 // indirect
+ golang.org/x/tools v0.37.0 // indirect
+)
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.sum b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.sum
new file mode 100644
index 00000000000..337edc95c60
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.sum
@@ -0,0 +1,58 @@
+github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
+github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
+github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
+github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
+github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
+github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
+github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
+github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
+github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
+go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
+golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
+golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
+golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
+golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
+golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
+golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
+golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
+golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
+golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+sigs.k8s.io/kubebuilder/v4 v4.9.0 h1:9e9LnQy/wQ24IZDIqye6iZZFOB9aKNyNfjnfsy3S8cw=
+sigs.k8s.io/kubebuilder/v4 v4.9.0/go.mod h1:Xql7wLeyXBQ4lJJdi1Pl8T/DeV4UXpA1kaOEumN0pzY=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/install.sh b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/install.sh
new file mode 100755
index 00000000000..f5244000d2d
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/install.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# Copyright 2021 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
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+PLUGIN_NAME="sampleexternalplugin"
+PLUGIN_VERSION="v1"
+PLUGIN_BINARY="./bin/${PLUGIN_NAME}"
+
+if [[ ! -f "${PLUGIN_BINARY}" ]]; then
+ echo "Plugin binary not found at ${PLUGIN_BINARY}"
+ echo "Make sure you run: make build"
+ exit 1
+fi
+
+# Detect OS and set plugin destination path
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ PLUGIN_DEST="$HOME/Library/Application Support/kubebuilder/plugins/${PLUGIN_NAME}/${PLUGIN_VERSION}"
+elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
+ PLUGIN_DEST="$HOME/.config/kubebuilder/plugins/${PLUGIN_NAME}/${PLUGIN_VERSION}"
+else
+ echo "Unsupported OS: $OSTYPE"
+ exit 1
+fi
+
+mkdir -p "${PLUGIN_DEST}"
+
+cp "${PLUGIN_BINARY}" "${PLUGIN_DEST}/${PLUGIN_NAME}"
+chmod +x "${PLUGIN_DEST}/${PLUGIN_NAME}"
+
+echo "Plugin installed at:"
+echo "${PLUGIN_DEST}/${PLUGIN_NAME}"
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/main.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/main.go
new file mode 100644
index 00000000000..d7f0f6fd482
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/main.go
@@ -0,0 +1,25 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "v1/cmd"
+)
+
+func main() {
+ cmd.Run()
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/api.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/api.go
new file mode 100644
index 00000000000..5d13d62631e
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/api.go
@@ -0,0 +1,116 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package scaffolds
+
+import (
+ "fmt"
+
+ "v1/scaffolds/internal/templates/api"
+
+ "github.com/spf13/pflag"
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin"
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+var ApiFlags = []external.Flag{
+ {
+ Name: "number",
+ Default: "1",
+ Type: "int",
+ Usage: "set a number to be added to the scaffolded apiFile.txt",
+ },
+ {
+ Name: "group",
+ Default: "",
+ Type: "string",
+ Usage: "API group name (e.g., 'example')",
+ },
+ {
+ Name: "version",
+ Default: "",
+ Type: "string",
+ Usage: "API version (e.g., 'v1alpha1')",
+ },
+ {
+ Name: "kind",
+ Default: "",
+ Type: "string",
+ Usage: "API kind (e.g., 'ExampleKind')",
+ },
+}
+
+var ApiMeta = plugin.SubcommandMetadata{
+ Description: "The `create api` subcommand of the sampleexternalplugin is meant to create an api for a project via Kubebuilder. It scaffolds a single file: `apiFile.txt`",
+ Examples: `
+ Scaffold with the defaults:
+ $ kubebuilder create api --plugins sampleexternalplugin/v1 --group samplegroup --version v1 --kind SampleKind
+
+ Scaffold with a specific number in the apiFile.txt file:
+ $ kubebuilder create api --plugins sampleexternalplugin/v1 --number 2 --group samplegroup --version v1 --kind SampleKind
+ `,
+}
+
+// ApiCmd handles all the logic for the `create api` subcommand of this sample external plugin
+func ApiCmd(pr *external.PluginRequest) external.PluginResponse {
+ pluginResponse := external.PluginResponse{
+ APIVersion: "v1alpha1",
+ Command: "create api",
+ Universe: pr.Universe,
+ }
+
+ flags := pflag.NewFlagSet("apiFlags", pflag.ContinueOnError)
+ flags.Int("number", 1, "set a number to be added in the scaffolded apiFile.txt")
+ flags.String("group", "", "API group name")
+ flags.String("version", "", "API version")
+ flags.String("kind", "", "API kind")
+
+ if err := flags.Parse(pr.Args); err != nil {
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ fmt.Sprintf("failed to parse flags: %s", err.Error()),
+ }
+ return pluginResponse
+ }
+
+ number, _ := flags.GetInt("number")
+ group, _ := flags.GetString("group")
+ version, _ := flags.GetString("version")
+ kind, _ := flags.GetString("kind")
+
+ // Validate GVK inputs
+ if group == "" || version == "" || kind == "" {
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ "--group, --version, and --kind are required flags",
+ }
+ return pluginResponse
+ }
+
+ // Scaffold API file using all values
+ apiFile := api.NewApiFile(
+ api.WithNumber(number),
+ api.WithGroup(group),
+ api.WithVersion(version),
+ api.WithKind(kind),
+ )
+
+ // Phase 2 Plugins uses the concept of a "universe" to represent the filesystem for a plugin.
+ // This universe is a key:value mapping of filename:contents. Here we are adding the file
+ // "apiFile.txt" to the universe with some content. When this is returned Kubebuilder will
+ // take all values within the "universe" and write them to the user's filesystem.
+ pluginResponse.Universe[apiFile.Name] = apiFile.Contents
+ return pluginResponse
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/init.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/init.go
new file mode 100644
index 00000000000..67525fdd102
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/init.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package scaffolds
+
+import (
+ "fmt"
+
+ "v1/scaffolds/internal/templates"
+
+ "github.com/spf13/pflag"
+
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin"
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+var InitFlags = []external.Flag{
+ {
+ Name: "domain",
+ Type: "string",
+ Default: "example.domain.com",
+ Usage: "sets the domain added in the scaffolded initFile.txt",
+ },
+}
+
+var InitMeta = plugin.SubcommandMetadata{
+ Description: "The `init` subcommand of the sampleexternalplugin is meant to initialize a project via Kubebuilder. It scaffolds a single file: `initFile.txt`",
+ Examples: `
+ Scaffold with the defaults:
+ $ kubebuilder init --plugins sampleexternalplugin/v1
+
+ Scaffold with a specific domain:
+ $ kubebuilder init --plugins sampleexternalplugin/v1 --domain sample.domain.com
+ `,
+}
+
+// InitCmd handles all the logic for the `init` subcommand of this sample external plugin
+func InitCmd(pr *external.PluginRequest) external.PluginResponse {
+ pluginResponse := external.PluginResponse{
+ APIVersion: "v1alpha1",
+ Command: "init",
+ Universe: pr.Universe,
+ }
+
+ // Here is an example of parsing a flag from a Kubebuilder external plugin request
+ flags := pflag.NewFlagSet("initFlags", pflag.ContinueOnError)
+ flags.String("domain", "example.domain.com", "sets the domain added in the scaffolded initFile.txt")
+ if err := flags.Parse(pr.Args); err != nil {
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ fmt.Sprintf("failed to parse flags: %s", err.Error()),
+ }
+ return pluginResponse
+ }
+ domain, _ := flags.GetString("domain")
+
+ initFile := templates.NewInitFile(templates.WithDomain(domain))
+
+ // Phase 2 Plugins uses the concept of a "universe" to represent the filesystem for a plugin.
+ // This universe is a key:value mapping of filename:contents. Here we are adding the file
+ // "initFile.txt" to the universe with some content. When this is returned Kubebuilder will
+ // take all values within the "universe" and write them to the user's filesystem.
+ pluginResponse.Universe[initFile.Name] = initFile.Contents
+
+ return pluginResponse
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/api/apiFile.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/api/apiFile.go
new file mode 100644
index 00000000000..58d895c2403
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/api/apiFile.go
@@ -0,0 +1,81 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package api
+
+import "fmt"
+
+// ApiFile represents the apiFile.txt
+type ApiFile struct {
+ Name string
+ Contents string
+ number int
+ group string
+ version string
+ kind string
+}
+
+// ApiFileOptions is a way to set configurable options for the API file
+type ApiFileOptions func(af *ApiFile)
+
+// WithNumber sets the number to be used in the resulting ApiFile
+func WithNumber(number int) ApiFileOptions {
+ return func(af *ApiFile) {
+ af.number = number
+ }
+}
+
+// WithGroup sets the group value
+func WithGroup(group string) ApiFileOptions {
+ return func(af *ApiFile) {
+ af.group = group
+ }
+}
+
+// WithVersion sets the version value
+func WithVersion(version string) ApiFileOptions {
+ return func(af *ApiFile) {
+ af.version = version
+ }
+}
+
+// WithKind sets the kind value
+func WithKind(kind string) ApiFileOptions {
+ return func(af *ApiFile) {
+ af.kind = kind
+ }
+}
+
+// NewApiFile returns a new ApiFile with
+func NewApiFile(opts ...ApiFileOptions) *ApiFile {
+ apiFile := &ApiFile{
+ Name: "apiFile.txt",
+ }
+
+ for _, opt := range opts {
+ opt(apiFile)
+ }
+
+ apiFile.Contents = fmt.Sprintf(apiFileTemplate,
+ apiFile.number, apiFile.group, apiFile.version, apiFile.kind)
+
+ return apiFile
+}
+
+const apiFileTemplate = `A simple text file created with the create api subcommand
+NUMBER: %d
+GROUP: %s
+VERSION: %s
+KIND: %s`
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/initFile.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/initFile.go
new file mode 100644
index 00000000000..e2bbdd43b1f
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/initFile.go
@@ -0,0 +1,52 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package templates
+
+import "fmt"
+
+// InitFile represents the InitFile.txt
+type InitFile struct {
+ Name string
+ Contents string
+ domain string
+}
+
+// InitFileOptions is a way to set configurable options for the Init file
+type InitFileOptions func(inf *InitFile)
+
+// WithDomain sets the number to be used in the resulting InitFile
+func WithDomain(domain string) InitFileOptions {
+ return func(inf *InitFile) {
+ inf.domain = domain
+ }
+}
+
+// NewInitFile returns a new InitFile with
+func NewInitFile(opts ...InitFileOptions) *InitFile {
+ initFile := &InitFile{
+ Name: "initFile.txt",
+ }
+
+ for _, opt := range opts {
+ opt(initFile)
+ }
+
+ initFile.Contents = fmt.Sprintf(initFileTemplate, initFile.domain)
+
+ return initFile
+}
+
+const initFileTemplate = "A simple text file created with the `init` subcommand\nDOMAIN: %s"
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/webhook/webhookFile.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/webhook/webhookFile.go
new file mode 100644
index 00000000000..ea1a4045e60
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/internal/templates/webhook/webhookFile.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package webhook
+
+// WebhookFile represents the WebhookFile.txt
+type WebhookFile struct {
+ Name string
+ Contents string
+ hooked bool
+}
+
+// WebhookFileOptions is a way to set configurable options for the Webhook file
+type WebhookFileOptions func(wf *WebhookFile)
+
+// WithHooked sets whether or not to add `HOOKED` to a new line in the resulting WebhookFile
+func WithHooked(hooked bool) WebhookFileOptions {
+ return func(wf *WebhookFile) {
+ wf.hooked = hooked
+ }
+}
+
+// NewWebhookFile returns a new WebhookFile with
+func NewWebhookFile(opts ...WebhookFileOptions) *WebhookFile {
+ webhookFile := &WebhookFile{
+ Name: "webhookFile.txt",
+ }
+
+ for _, opt := range opts {
+ opt(webhookFile)
+ }
+
+ webhookFile.Contents = WebhookFileDefaultMessage
+
+ if webhookFile.hooked {
+ webhookFile.Contents += WebhookFileHookedMessage
+ }
+
+ return webhookFile
+}
+
+const WebhookFileDefaultMessage = "A simple text file created with the `create webhook` subcommand"
+const WebhookFileHookedMessage = "\nHOOKED!"
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/webhook.go b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/webhook.go
new file mode 100644
index 00000000000..ec13dc56205
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/scaffolds/webhook.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2022 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
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package scaffolds
+
+import (
+ "fmt"
+
+ "github.com/spf13/pflag"
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin"
+ "sigs.k8s.io/kubebuilder/v4/pkg/plugin/external"
+)
+
+var WebhookFlags = []external.Flag{
+ {
+ Name: "hooked",
+ Type: "bool",
+ Default: "false",
+ Usage: "add the word `hooked` to the end of the scaffolded webhookFile.txt",
+ },
+}
+
+var WebhookMeta = plugin.SubcommandMetadata{
+ Description: "The `create webhook` subcommand of the sampleexternalplugin is meant to create a webhook for a project via Kubebuilder. It scaffolds a single file: `webhookFile.txt`",
+ Examples: `
+ Scaffold with the defaults:
+ $ kubebuilder create webhook --plugins sampleexternalplugin/v1
+
+ Scaffold with the text "HOOKED!" in the webhookFile.txt file:
+ $ kubebuilder create webhook --plugins sampleexternalplugin/v1 --hooked
+ `,
+}
+
+// WebhookCmd handles all the logic for the `create webhook` subcommand of this sample external plugin
+func WebhookCmd(pr *external.PluginRequest) external.PluginResponse {
+ pluginResponse := external.PluginResponse{
+ APIVersion: "v1alpha1",
+ Command: "create webhook",
+ Universe: pr.Universe,
+ }
+
+ // Here is an example of parsing a flag from a Kubebuilder external plugin request
+ flags := pflag.NewFlagSet("apiFlags", pflag.ContinueOnError)
+ flags.Bool("hooked", false, "add the word `hooked` to the end of the scaffolded webhookFile.txt")
+ if err := flags.Parse(pr.Args); err != nil {
+ pluginResponse.Error = true
+ pluginResponse.ErrorMsgs = []string{
+ fmt.Sprintf("failed to parse flags: %s", err.Error()),
+ }
+ return pluginResponse
+ }
+ hooked, _ := flags.GetBool("hooked")
+
+ msg := "A simple text file created with the `create webhook` subcommand"
+ if hooked {
+ msg += "\nHOOKED!"
+ }
+
+ // Phase 2 Plugins uses the concept of a "universe" to represent the filesystem for a plugin.
+ // This universe is a key:value mapping of filename:contents. Here we are adding the file
+ // "webhookFile.txt" to the universe with some content. When this is returned Kubebuilder will
+ // take all values within the "universe" and write them to the user's filesystem.
+ pluginResponse.Universe["webhookFile.txt"] = msg
+
+ return pluginResponse
+}
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/test/test.sh b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/test/test.sh
new file mode 100755
index 00000000000..3e55684d2be
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/test/test.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright 2021 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
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo "Setting up test environment..."
+
+# Create a directory named testplugin for running kubebuilder commands
+mkdir -p testdata/testplugin
+cd testdata/testplugin
+rm -rf *
+
+# Run Kubebuilder commands inside the testplugin directory
+kubebuilder init --plugins sampleexternalplugin/v1 --domain sample.domain.com
+kubebuilder create api --plugins sampleexternalplugin/v1 --number 2 --group samplegroup --version v1 --kind SampleKind
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/PROJECT b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/PROJECT
new file mode 100644
index 00000000000..6f68fdd05c2
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/PROJECT
@@ -0,0 +1,8 @@
+# Code generated by tool. DO NOT EDIT.
+# This file is used to track the info used to scaffold your project
+# and allow the plugins properly work.
+# More info: https://book.kubebuilder.io/reference/project-config.html
+cliVersion: 4.6.0
+layout:
+- sampleexternalplugin/v1
+version: "3"
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/apiFile.txt b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/apiFile.txt
new file mode 100644
index 00000000000..d8d1881855d
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/apiFile.txt
@@ -0,0 +1,5 @@
+A simple text file created with the create api subcommand
+NUMBER: 2
+GROUP: samplegroup
+VERSION: v1
+KIND: SampleKind
\ No newline at end of file
diff --git a/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/initFile.txt b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/initFile.txt
new file mode 100644
index 00000000000..de958e019b3
--- /dev/null
+++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/testdata/testplugin/initFile.txt
@@ -0,0 +1,2 @@
+A simple text file created with the `init` subcommand
+DOMAIN: sample.domain.com
\ No newline at end of file
diff --git a/docs/book/src/versions_compatibility_supportability.md b/docs/book/src/versions_compatibility_supportability.md
new file mode 100644
index 00000000000..b388da3e339
--- /dev/null
+++ b/docs/book/src/versions_compatibility_supportability.md
@@ -0,0 +1,50 @@
+# Versions Compatibility and Supportability
+
+Projects created by Kubebuilder contain a `Makefile` that installs tools at versions defined during project creation.
+The main tools included are:
+
+- [kustomize](https://github.com/kubernetes-sigs/kustomize)
+- [controller-gen](https://github.com/kubernetes-sigs/controller-tools)
+- [setup-envtest](https://github.com/kubernetes-sigs/controller-runtime/tree/main/tools/setup-envtest)
+
+Additionally, these projects include a `go.mod` file specifying dependency versions.
+Kubebuilder relies on [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) and its Go and Kubernetes dependencies.
+Therefore, the versions defined in the `Makefile` and `go.mod` files are the ones that have been tested, supported, and recommended.
+
+Each minor version of Kubebuilder is tested with a specific minor version of client-go.
+While a Kubebuilder minor version *may* be compatible with other client-go minor versions,
+or other tools this compatibility is not guaranteed, supported, or tested.
+
+The minimum Go version required by Kubebuilder is determined by the highest minimum
+Go version required by its dependencies. This is usually aligned with the minimum
+Go version required by the corresponding `k8s.io/*` dependencies.
+
+Compatible `k8s.io/*` versions, client-go versions, and minimum Go versions can be found in the `go.mod`
+file scaffolded for each project for each [tag release](https://github.com/kubernetes-sigs/kubebuilder/tags).
+
+**Example:** For the `4.1.1` release, the minimum Go version compatibility is `1.22`.
+You can refer to the samples in the testdata directory of the tag released [v4.1.1](https://github.com/kubernetes-sigs/kubebuilder/tree/v4.1.1/testdata),
+such as the [go.mod](https://github.com/kubernetes-sigs/kubebuilder/blob/v4.1.1/testdata/project-v4/go.mod#L3) file for `project-v4`. You can also check the tools versions supported and
+tested for this release by examining the [Makefile](https://github.com/kubernetes-sigs/kubebuilder/blob/v4.1.1/testdata/project-v4/Makefile#L160-L165).
+
+## Operating Systems Supported
+
+Currently, Kubebuilder officially supports macOS and Linux platforms. If you are using a Windows OS, we recommend you read the instructions in [here](https://github.com/kubernetes-sigs/kubebuilder/blob/master/docs/windows.md).
+
+Contributions towards supporting Windows are not planned.
+
+
+ Project customizations
+
+After using the CLI to create your project, you are free to customize how
+you see fit. Bear in mind, that it is not recommended to deviate from
+the proposed layout unless you know what you are doing.
+
+For example, you should refrain from moving the scaffolded files,
+doing so will make it difficult in upgrading your project in the future.
+You may also lose the ability to use some of the CLI features and helpers.
+For further information on the project layout, see the doc [What's in a basic project?][basic-project-doc]
+
+
+
+[basic-project-doc]: ./cronjob-tutorial/basic-project.md
diff --git a/docs/book/theme/css/custom.css b/docs/book/theme/css/custom.css
index 3678cc320c2..41f57da1671 100644
--- a/docs/book/theme/css/custom.css
+++ b/docs/book/theme/css/custom.css
@@ -1,30 +1,46 @@
-.menu-title img {
- vertical-align: bottom;
+/* Adds a thin border to the sidebar (aesthetics) */
+#sidebar {
+ border-right: 1px solid var(--theme-popup-border);
}
-#notice-bar {
- background: var(--bg);
- padding: 1em;
- margin-left: calc(-1 * var(--page-padding));
- margin-right: calc(-1 * var(--page-padding));
- margin-bottom: 1em;
+/* Sets the size of the logo on the menu bar */
+.large-logo-img {
+ height: 50px;
+}
+
+/* Centers the logo on the menu bar */
+.menu-title {
+ display: flex;
+ align-items: center; /* vertical centering */
+ justify-content: center; /* horizontal centering */
+}
- border-bottom: 1px solid var(--table-border-color);
- box-shadow: 0 1px 5px 0 var(--table-border-color);
+/* Fixes first header sliding under the menu bar when selected */
+.content {
+ overflow-y: clip;
}
-#notice-bar * {
- color: var(--fg);
+/* Fixes contrast in codeblocks */
+pre > .hljs {
+ border: 1px solid var(--theme-popup-border);
+ border-radius: 6px;
}
-#notice-bar a {
- text-decoration: none;
+/* Fixes scrollbar background color */
+html {
+ scrollbar-color: var(--scrollbar) transparent;
}
-#notice-bar a:not(.header) {
- color: var(--links);
+/* Fixes links formatting */
+.content a {
+ text-decoration: solid underline var(--links);
}
-#notice-bar h2 {
- margin-top: 0;
+/* custom light theme */
+.light {
+ --sidebar-bg: #eef5ff;
+ --sidebar-fg: #334155;
+ --sidebar-non-existant: #7b8da8;
+ --sidebar-active: #466bb3;
+ --sidebar-spacer: #c9d3e0;
}
diff --git a/docs/book/theme/css/markers.css b/docs/book/theme/css/markers.css
index 67b74870b2c..e502f64a71c 100644
--- a/docs/book/theme/css/markers.css
+++ b/docs/book/theme/css/markers.css
@@ -15,13 +15,8 @@
margin-bottom: 0.25em;
}
-/* the marker name */
-.marker > dt.name::before {
- content: '// +';
-}
.marker > dt.name {
font-weight: bold;
- order: 0; /* hack around the ::before's positioning to get it after the line */
}
/* the target blob */
@@ -217,6 +212,11 @@ input.markers-summarize:checked ~ label.markers-summarize::before {
margin-top: 0.125em;
}
+/* Completely hide low-value collapsed sections (license text, imports) */
+details.collapse-hide {
+ display: none;
+}
+
/* details elements (not markers) */
details.collapse-code > summary {
width: 100%;
@@ -253,6 +253,10 @@ details.collapse-code[open] > summary pre span::after {
content: "";
}
+details.collapse-code > summary pre span.collapse-summary::before {
+ content: "// ";
+}
+
details.collapse-code > summary pre span::before {
content: "// ";
}
@@ -370,11 +374,6 @@ cite.literate-source > a::before {
color: var(--fg);
}
-/* hide the annoying "copy to clipboard" buttons */
-.literate pre > .buttons {
- display: none;
-}
-
/* add a bit of extra padding for readability */
.literate pre code {
padding-top: 0.75em;
diff --git a/docs/book/theme/css/version-dropdown.css b/docs/book/theme/css/version-dropdown.css
new file mode 100644
index 00000000000..d3ac4edb0c6
--- /dev/null
+++ b/docs/book/theme/css/version-dropdown.css
@@ -0,0 +1,23 @@
+.version-dropdown-content {
+ display: none;
+ position: absolute;
+ background-color: #f9f9f9;
+ min-width: 90px;
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
+ z-index: 1;
+ }
+
+ .version-dropdown-content a {
+ color: black;
+ padding: 12px 16px;
+ text-decoration: none;
+ display: block;
+ }
+
+ .version-dropdown-content a:hover {
+ background-color: #f1f1f1;
+ }
+
+ .version-dropdown:hover .version-dropdown-content {
+ display: block;
+ }
\ No newline at end of file
diff --git a/docs/book/theme/header.hbs b/docs/book/theme/header.hbs
deleted file mode 100644
index 586f6da651f..00000000000
--- a/docs/book/theme/header.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
diff --git a/docs/book/theme/index.hbs b/docs/book/theme/index.hbs
index 51a386cf0eb..ffced82e4d5 100644
--- a/docs/book/theme/index.hbs
+++ b/docs/book/theme/index.hbs
@@ -21,7 +21,7 @@
-
+
@@ -83,14 +83,17 @@
html.classList.add('js');
+
@@ -108,27 +111,36 @@
- {{> header}}
-
{{#if search_enabled}}