diff --git a/.github/*.instructions.md b/.github/*.instructions.md new file mode 100644 index 00000000000..b37eb3af2e3 --- /dev/null +++ b/.github/*.instructions.md @@ -0,0 +1 @@ +See [AGENTS.md](../AGENTS.md) for AI agent guidelines. \ No newline at end of file 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..494f7e82917 100644 --- a/docs/book/book.toml +++ b/docs/book/book.toml @@ -5,12 +5,20 @@ 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"] +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..1645dbb07dd 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -2,10 +2,13 @@ [Introduction](./introduction.md) +[Architecture](./architecture.md) + [Quick Start](./quick-start.md) -[Architecture](./architecture.md) +[Getting Started](./getting-started.md) +[Versions Compatibility and Supportability](./versions_compatibility_supportability.md) --- - [Tutorial: Building CronJob](cronjob-tutorial/cronjob-tutorial.md) @@ -16,7 +19,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,7 +29,7 @@ - [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) @@ -39,72 +42,100 @@ - [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) --- - [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) - } -} -``` - - - -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 -``` - - - - -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 - - - -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. - - - - -## 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 ``` Now that we've got a project in place, let's take a look at what Kubebuilder has scaffolded for us so far... + + + +[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 index 0e9be7c0264..36d49abd519 100644 --- a/docs/book/src/cronjob-tutorial/epilogue.md +++ b/docs/book/src/cronjob-tutorial/epilogue.md @@ -2,14 +2,14 @@ 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. +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™: +a tutorial section on them Soon™: - adding [additional printer columns][printer-columns] `kubectl get` 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. - - 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..d38574ccb22 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: + + + ## 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 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 ``` + + 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. - - - 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. + + + +## 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: + +Example Issue + +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: + +Screenshot 2025-08-26 at 13 40 53 + +If conflicts arise, AI-generated comments call them out and provide next steps, such as: + +Conflicts + +### 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 + + + +[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. + + + + +## 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" + ``` + + + +## 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. + + + +## 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]. + + + +## 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] + + + +### 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: + +![output](https://user-images.githubusercontent.com/18136486/175382307-9a6c3b8b-6cc7-4339-b221-2539d0fec042.gif) + +#### 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..d5f5c5f89c6 --- /dev/null +++ b/docs/book/src/plugins/available/helm-v1-alpha.md @@ -0,0 +1,104 @@ +# Helm Plugin (`helm/v1-alpha`) - **DEPRECATED** + + + +The Helm plugin is an optional plugin that can be used to scaffold a Helm chart, allowing you to distribute the project using Helm. + +By default, users can generate a bundle with all the manifests by running the following command: + +```bash +make build-installer IMG=/ +``` + +This allows the project consumer to install the solution by applying the bundle with: + +```bash +kubectl apply -f https://raw.githubusercontent.com//project-v4//dist/install.yaml +``` + +However, in many scenarios, you might prefer to provide a Helm chart to package your solution. +If so, you can use this plugin to generate the Helm chart under the `dist` directory. + + + +## When to use it + +- If you want to provide a Helm chart for users to install and manage your project. +- If you need to update the Helm chart generated under `dist/chart/` with the latest project changes: + - After generating new manifests, use the `edit` option to sync the Helm chart. + - **IMPORTANT:** If you have created a webhook or an API using the [DeployImage][deployImage-plugin] plugin, + you must run the `edit` command with the `--force` flag to regenerate the Helm chart values based + on the latest manifests (_after running `make manifests`_) to ensure that the HelmChart values are + updated accordingly. In this case, if you have customized the files + under `dist/chart/values.yaml`, and the `templates/manager/manager.yaml`, you will need to manually reapply your customizations on top + of the latest changes after regenerating the Helm chart. + +## How to use it ? + +### Basic Usage + +The Helm plugin is attached to the `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..1eefec39f70 --- /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 +``` + + + +## 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`) | + + 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. + + + +## 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]`) + + + +## 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. - - - -## 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. + + + +## 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..5010928f5b9 --- /dev/null +++ b/docs/book/src/plugins/extending/external-plugins.md @@ -0,0 +1,159 @@ +# 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 + +`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`. + +**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"] +} +``` + +**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 chain. This allows external plugins to determine which other plugins are being used. For example, a plugin can check if `go.kubebuilder.io/v4` or `go.kubebuilder.io/v3` is in the chain to adjust its scaffolding accordingly. + +### 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": [] +} +``` + + + +## 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. + + + +### 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. + + + +## 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]`) + + + +## 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`. + + + + +[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:** - +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..292647c90ff 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.5+ - [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. @@ -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/ ``` @@ -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 {

+ + ## 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. 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 ``` @@ -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. + + + +### 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..f37e4d47bc3 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,81 @@ 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). + + + +## Handling Resource Status in Admission Webhooks + + + +### 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..0bba162ef15 --- /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. + + + +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..d5735316db4 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}`. + + + + + +[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..4bf73a7e03f --- /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. + + + +## 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. + + + +## 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..7902bef7d36 --- /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`. + + + +## 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: + +Example Issue + +With `--use-gh-models`, an AI comment highlights key changes and suggests how to resolve any conflicts: + +Comment + +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 +``` + + + +## 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 + + + + + +## 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 +- 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 - + +## 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 dc958ebda67..757efd7ca6d 100644 --- a/docs/book/src/reference/envtest.md +++ b/docs/book/src/reference/envtest.md @@ -1,5 +1,76 @@ # Configuring envtest for integration tests -[`controller-runtime`](http://sigs.k8s.io/controller-runtime) offers `envtest` ([godoc](https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/envtest?tab=doc)), a package that 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 + +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)) +``` + +## 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`. + +### Download the binaries +`make envtest` will download the `setup-envtest` binary to `./bin/`. +```shell +make envtest +``` + +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 +./bin/setup-envtest use 1.31.0 +``` + +### 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 + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -i --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out +``` + +NOTE: The `ENVTEST_K8S_VERSION` needs to match the `setup-envtest` you downloaded above. Otherwise, you will see an error like the below +```sh +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 Using `envtest` in integration tests follows the general flow of: @@ -24,31 +95,50 @@ err = testEnv.Stop() Logs from the test runs are prefixed with `test-env`. + + ### 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{} @@ -67,7 +157,15 @@ var _ = AfterSuite(func() { Expect(os.Unsetenv("TEST_ASSET_KUBECTL")).To(Succeed()) }) -``` +``` + + ### 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`: @@ -102,3 +200,159 @@ expectedOwnerReference := v1.OwnerReference{ } Expect(deployment.ObjectMeta.OwnerReferences).To(ContainElement(expectedOwnerReference)) ``` + + + +## 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, +} +``` + + + +[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` - - - ## 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. + + + +[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 ``` + + 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. + + + {{#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..ab68f4f02db --- /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. + + + +## 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. | + + + + + + + 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..87465131343 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`. + + +## 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. + + + +#### 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 +``` + + + +### 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 +``` + + + +## 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) + + + + + +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 ``` + + 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. + + + +Those metrics will be available for prometheus or other openmetrics systems to scrape. + +![Screen Shot 2021-06-14 at 10 15 59 AM](https://user-images.githubusercontent.com/37827279/121932262-8843cd80-ccf9-11eb-9c8e-98d0eda80169.png) + + \ 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{{ + ... + }}, + }, +``` + + + +## 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 +``` + + + +## 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. + + + +[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..c2af98c9678 --- /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. + + + +## 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: + + ![pprof-result-visualization](./images/pprof-result-visualization.png) + +[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..d04ec87c479 100644 --- a/docs/book/src/reference/project-config.md +++ b/docs/book/src/reference/project-config.md @@ -2,7 +2,75 @@ ## 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 +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 +82,89 @@ 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 +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.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`. + + + +## 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]) + + + + + +### 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/submodule-layouts.md b/docs/book/src/reference/submodule-layouts.md new file mode 100644 index 00000000000..0c9c8c1c79b --- /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. + + + +## 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. + + + +## 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. + + + +#### 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..f032cc28c05 --- /dev/null +++ b/docs/book/src/reference/using_an_external_resource.md @@ -0,0 +1,179 @@ +# 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 +``` + +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 + +Following an example: + +```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 +``` + +## 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 ... -``` - - 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..f95b27dc6bd --- /dev/null +++ b/docs/book/src/simple-external-plugin-tutorial/testdata/sampleexternalplugin/v1/go.mod @@ -0,0 +1,17 @@ +module v1 + +go 1.24.5 + +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..a5be610af5b --- /dev/null +++ b/docs/book/src/versions_compatibility_supportability.md @@ -0,0 +1,49 @@ +# 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, you may encounter issues. +Contributions towards supporting Windows are welcome + + + +[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..de1d7183c30 100644 --- a/docs/book/theme/css/custom.css +++ b/docs/book/theme/css/custom.css @@ -2,29 +2,6 @@ vertical-align: bottom; } -#notice-bar { - background: var(--bg); - padding: 1em; - margin-left: calc(-1 * var(--page-padding)); - margin-right: calc(-1 * var(--page-padding)); - margin-bottom: 1em; - - border-bottom: 1px solid var(--table-border-color); - box-shadow: 0 1px 5px 0 var(--table-border-color); -} - -#notice-bar * { - color: var(--fg); -} - -#notice-bar a { - text-decoration: none; -} - -#notice-bar a:not(.header) { - color: var(--links); -} - -#notice-bar h2 { - margin-top: 0; +#sidebar-toggle-anchor:checked .page-wrapper { + margin-inline-start: calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)); } diff --git a/docs/book/theme/css/markers.css b/docs/book/theme/css/markers.css index 67b74870b2c..44c4e92ebbf 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 */ 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 @@ -
-

Black lives matter.

-

We stand in solidarity with the Black community.

-

Racism is unacceptable.

-

It conflicts with the core values of the Kubernetes project and our community does not tolerate it.

-

-
diff --git a/docs/book/theme/index.hbs b/docs/book/theme/index.hbs index 51a386cf0eb..f98eacc1da1 100644 --- a/docs/book/theme/index.hbs +++ b/docs/book/theme/index.hbs @@ -83,14 +83,17 @@ html.classList.add('js'); + @@ -108,9 +111,9 @@ - {{> header}} - {{#if search_enabled}}