-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This introduces a new version of the book. The initial portion is built around a tutorial building the cronjob controller. It now uses [mdbook](https://crates.io/crates/mdbook), which is like gitbook but maintained and written in rust. We've got a custom "plugin" (executable) that slurps Go files into book pages for parts of the tutorial (look for `{{#literatego ./path/to/thing}}`).
- Loading branch information
1 parent
e46e06e
commit 52ba0c3
Showing
69 changed files
with
8,690 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[book] | ||
authors = ["The Kubebuilder Maintainers"] | ||
multilingual = false | ||
src = "src" | ||
title = "The Kubebuilder Book" | ||
|
||
[output.html] | ||
google-analytics = "UA-119864590-1" | ||
curly-quotes = true | ||
|
||
[preprocessor.literatego] | ||
command = "./litgo.sh" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/bin/bash | ||
|
||
os=$(go env GOOS) | ||
arch=$(go env GOARCH) | ||
|
||
# grab mdbook | ||
# mdbook's deploy got borked by a CI move, so grab our build till that gets | ||
# fixed (https://github.com/rust-lang-nursery/mdBook/issues/904). | ||
curl -sL -o /tmp/mdbook https://storage.googleapis.com/kubebuilder-build-tools/mdbook-0.2.3-${os}-${arch} | ||
chmod +x /tmp/mdbook | ||
/tmp/mdbook build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/bin/bash | ||
|
||
set -ex | ||
|
||
( | ||
pushd ./utils | ||
go build -o ../../../bin/literate-go ./literate.go | ||
popd | ||
) &>/dev/null | ||
|
||
../../bin/literate-go "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Summary | ||
|
||
[Introduction](./introduction.md) | ||
|
||
[Quick Start](./quick-start.md) | ||
|
||
--- | ||
|
||
- [Tutorial: Building CronJob](./cronjob-tutorial.md) | ||
|
||
- [What's in a basic project?](./cronjob-tutorial/basic-project.md) | ||
- [Every journey needs a start, every program a main](./cronjob-tutorial/empty-main.md) | ||
- [Groups and Versions and Kinds, oh my!](./cronjob-tutorial/gvks.md) | ||
- [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) | ||
|
||
- [What's in a controller?](./cronjob-tutorial/controller-overview.md) | ||
- [Implementing a controller](./cronjob-tutorial/controller-implementation.md) | ||
|
||
- [You said something about main?](./cronjob-tutorial/main-revisited.md) | ||
|
||
- [Running and deploying the controller](./cronjob-tutorial/running.md) | ||
- [Epilogue](./cronjob-tutorial/epilogue.md) | ||
|
||
--- | ||
|
||
[Appendix: The TODO Landing Page](./TODO.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# TODO | ||
|
||
If you're seeing this page, it's probably because something's not done in | ||
the book yet. 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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Tutorial: Building CronJob | ||
|
||
Too many tutorials start out with some really contrived setup, or some toy | ||
application that gets the basics across, and then stalls out on the more | ||
complicated suff. Instead, this tutorial should take you through (almost) | ||
the full gamut of complexity with Kubebuilder, starting off simple and | ||
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 Kuberntes, and we'd like to | ||
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 the by | ||
bulding on top of the *Job* controller, whose task is to run one-off tasks | ||
once, seeing them to completion. | ||
|
||
Instead of trying to tackle rewriting the Job controller as well, we'll | ||
use this as an opportunity to see how to interact with external types. | ||
|
||
## Scaffolding Out Our Project | ||
|
||
As covered in the [quick start](./quick-start.md), we'll need to scaffold | ||
out a new project. Make sure you've [installed | ||
Kubebuilder](./quick-start.md#installation), then scaffold out a new | ||
project: | ||
|
||
```bash | ||
# we'll use a domain of tutorial.kubebuilder.io, | ||
# so all API groups will be <group>.tutorial.kubebuilder.io. | ||
kubebuilder init --domain tutorial.kubebuilder.io | ||
``` | ||
|
||
Now that we've got've a project in place, let's take a look at what | ||
Kubebuilder has scaffolded for us so far... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Designing an API | ||
|
||
In Kubernetes, we have a few rules for how we design APIs. Namely, all | ||
serialized fields *must* be `camelCase`, so we use JSON struct tags to | ||
specify this. We can also use the `omitempty` struct tag to mark that | ||
a field should be omitted from serialization when empty. | ||
|
||
Fields may use most of the primitive types. Numbers are the exception: | ||
for API compatibility purposes, we accept two forms of numbers: `int32` | ||
for integers, and `resource.Quantity` for decimals. | ||
|
||
<details><summary>Hold up, what's a Quantity?</summary> | ||
|
||
Quantities are a special notation for decimal numbers that have an | ||
explicitly fixed representation that makes them more portable across | ||
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 serialize, human readable for | ||
uses whole numbers and suffixes to specify values much the way we describe | ||
computer storage. | ||
|
||
For instance, the value `2m` means `0.002` in decimal notation. `2Ki` | ||
means `2048` in decimal, while `2K` means `2000` in decimal. If we want | ||
to specify fractions, we switch to a suffix that lets us use a whole | ||
number: `2.5` is `2500m`. | ||
|
||
There are two supported bases: 10 and 2 (called decimal and binary, | ||
respectively). Decimal base is indicated with "normal" SI suffixes (e.g. | ||
`M` and `K`), while Binary base is specified in "mebi" notation (e.g. `Mi` | ||
and `Ki`). Think [megabytes vs mebibytes](../TODO.md). | ||
|
||
</details> | ||
|
||
There's one other special type that we use: `metav1.Time`. This functions | ||
identically to `time.Time`, except that it has a fixed, portable | ||
serialization format. | ||
|
||
With that out of the way, let's take a look at what our CronJob object | ||
looks like! | ||
|
||
{{#literatego ./testdata/project/api/v1/cronjob_types.go}} | ||
|
||
Now that we have an API, we'll need to write a controller to actually | ||
implement the functionality. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# What's in a basic project? | ||
|
||
When scaffolding out a new project, Kubebuilder provides us with a few | ||
basic pieces of boilerplate. | ||
|
||
## Build Infrastructure | ||
|
||
First up, basic infrastructure for building you project: | ||
|
||
<details><summary>`go.mod`: A new Go module matching our project, with basic dependencies</summary> | ||
|
||
```go | ||
{{#include ./testdata/project/go.mod}} | ||
``` | ||
</details> | ||
|
||
<details><summary>`Makefile`: Make targets for building and deploying your controller</summary> | ||
```makefile | ||
{{#include ./testdata/project/Makefile}} | ||
``` | ||
</details> | ||
|
||
<details><summary>`PROJECT`: Kubebuilder metadata for scaffolding new components</summary> | ||
```yaml | ||
{{#include ./testdata/project/PROJECT}} | ||
``` | ||
</details> | ||
|
||
## Launch Configuration | ||
|
||
We also get launch configuration under the | ||
[`config/`](https://sigs.k8s.io/kubebuilder.io/docs/book/cronjob-tutorial/testdata/project/config) | ||
directory. Right now, it just contains | ||
[Kustomize](https://sigs.k8s.io/kustomize) YAML definitions required to | ||
launch our controller on a cluster, but once we get started writing our | ||
controller, it'll also hold our CustomResourceDefinitions, RBAC | ||
configuration, and WebhookConfigurations. | ||
|
||
[`config/default`](../TODO.md) contains a [Kustomize base](../TODO.md) for launching | ||
the controller in a standard configuration. | ||
|
||
Each other directory contains a different piece of configuration, | ||
refactored out into its own base: | ||
|
||
- [`config/manager`](../TODO.md): launch your controllers as pods in the | ||
cluster | ||
|
||
- [`config/rbac`](../TODO.md): permissions required to run your | ||
controllers under their own service account | ||
|
||
## The Entrypoint | ||
|
||
Last, but certainly not least, Kubebuilder scaffolds out the basic | ||
entrypoint of our project: `main.go`. Let's take a look at that next... |
24 changes: 24 additions & 0 deletions
24
docs/book/src/cronjob-tutorial/controller-implementation.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Implementing a controller | ||
|
||
The basic logic of our CronJob controller is this: | ||
|
||
1. Load the named CronJob | ||
|
||
2. List all active jobs, and update the status | ||
|
||
3. Clean up old jobs according to the history limits | ||
|
||
4. Check if we're supsended (and don't do anything else if we are) | ||
|
||
5. Get the next scheduled run | ||
|
||
6. Run a new job if it's on schedule, not past the deadline, and not | ||
blocked by our concurrency policy | ||
|
||
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}} | ||
|
||
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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# What's in a controller? | ||
|
||
Controllers are the core of Kubernetes, and of any operator. | ||
|
||
It's a controller's job to ensure that, for any given object, the actual | ||
state of the world (both the cluster state, and potentially external state | ||
like running containers for Kubelet or loadbalancers for a cloud provider) | ||
matches the desired state in the object. Each controller focuses on one | ||
*root* Kind, but may interact with other Kinds. | ||
|
||
We call this process *reconciling*. | ||
|
||
In controller-runtime, the logic that implements the reconciling for | ||
a specific kind is called a [*Reconciler*](../TODO.md). A reconciler | ||
takes the name of an object, and returns whether or not we need to try | ||
again (e.g. in case of errors or periodic controllers, like the | ||
HorizontalPodAutoscaler). | ||
|
||
{{#literatego ./testdata/emptycontroller.go}} | ||
|
||
Now that we've seen the basic structure of a reconciler, let's fill out | ||
the logic for `CronJob`s. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Every journey needs a start, every program a main | ||
|
||
{{#literatego ./testdata/emptymain.go}} | ||
|
||
With that out of the way, we can get on to scaffolding our API! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Epilogue | ||
|
||
Things left to do: | ||
|
||
- Write custom printer columns | ||
- Discuss webhooks | ||
- Use different watches |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Groups and Versions and Kinds, oh my! | ||
|
||
Actually, before we get started with our API, we should talk terminology | ||
a bit. | ||
|
||
When we talk about APIs in Kubernetes, we often use 4 terms: *groups*, | ||
*versions*, *kinds*, and *resources*. | ||
|
||
## Groups and Versions | ||
|
||
An *API Group* in Kubernetes is simply a collection of related | ||
functionality. Each group has one or more *versions*, which, as the name | ||
suggests, allow us to change how an API works over time. | ||
|
||
## Kinds and Resources | ||
|
||
Each API group-version contains one or more API types, which we call | ||
*Kinds*. While a Kind may change forms between versions, each form must | ||
be able to store all the data of the other forms, somehow (we can store | ||
the data in fields, or in annotations). This means that using an older | ||
API version won't cause newer data to be lost or corrupted. See the | ||
[Kubernetes API guidelines](../TODO.md) for more information. | ||
|
||
You'll also hear mention of *resources* on occaison. A resource is simply | ||
a use of a Kind in the API. Often, there's a one-to-one mapping between | ||
Kinds and resources. For instance, the `pods` resource corresponds to the | ||
`Pod` Kind. However, sometimes, the same Kind may be returned by multiple | ||
resources. For instance, the `Scale` Kind is returned by all scale | ||
subresources, like `deployments/scale` or `replicasets/scale`. This is | ||
what allows the Kubernetes HorizontalPodAutoscaler to interact with | ||
different resources. With CRDs, however, each Kind will correspond to | ||
a single resource. | ||
|
||
Notice that resources are always lowercase, and by convention are the | ||
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 | ||
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. | ||
|
||
Now that we have our terminology straight, we can *actually* create our | ||
API! | ||
|
||
## Err, but what's that Scheme thing? | ||
|
||
The `Scheme` we saw before is simply a way to keep track of what Go type | ||
corresponds to a given GVK. | ||
|
||
For instance, suppose we mark that 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`). | ||
|
||
Then, we can later construct a new `&CronJob{}` given some JSON from the | ||
API server that says | ||
|
||
```json | ||
{ | ||
"kind": "CronJob", | ||
"apiVersion": "batch.tutorial.kubebuilder.io/v1", | ||
... | ||
} | ||
``` | ||
|
||
or properly look up the group-version when we go to submit a `&CronJob{}` | ||
in an update. |
Oops, something went wrong.