Skip to content

Commit

Permalink
Merge pull request #295 from DirectXMan12/docs/overview-and-fixup
Browse files Browse the repository at this point in the history
📖 Add project overview to docs, clean up docs, structure
  • Loading branch information
k8s-ci-robot committed Feb 13, 2019
2 parents 6443f37 + cb0aefb commit 45d6520
Show file tree
Hide file tree
Showing 50 changed files with 671 additions and 124 deletions.
81 changes: 81 additions & 0 deletions FAQ.md
@@ -0,0 +1,81 @@
# FAQ

### Q: How do I know which type of object a controller references?

**A**: Each controller should only reconcile one object type. Other
affected objects should be mapped to a single type of root object, using
the `EnqueueRequestForOwner` or `EnqueueRequestsFromMapFunc` event
handlers, and potentially indicies. Then, your Reconcile method should
attempt to reconcile *all* state for that given root objects.

### Q: How do I have different logic in my reconciler for different types of events (e.g. create, update, delete)?

**A**: You should not. Reconcile functions should be idempotent, and
should always reconcile state by reading all the state it needs, then
writing updates. This allows your reconciler to correctly respond to
generic events, adjust to skipped or coalesced events, and easily deal
with application startup. The controller will enqueue reconcile requests
for both old and new objects if a mapping changes, but it's your
responsibility to make sure you have enough information to be able clean
up state that's no longer referenced.

### Q: My cache might be stale if I read from a cache! How should I deal with that?

**A**: There are several different approaches that can be taken, depending
on your situation.

- When you can, take advantage of optimistic locking: use deterministic
names for objects you create, so that the Kubernetes API server will
warn you if the object already exists. Many controllers in Kubernetes
take this approach: the StatefulSet controller appends a specific number
to each pod that it creates, while the Deployment controller hashes the
pod template spec and appends that.

- In the few cases when you cannot take advantage of deterministic names
(e.g. when using generateName), it may be useful in to track which
actions you took, and assume that they need to be repeated if they don't
occur after a given time (e.g. using a requeue result). This is what
the ReplicaSet controller does.

In general, write your controller with the assumption that information
will eventually be correct, but may be slightly out of date. Make sure
that your reconcile function enforces the entire state of the world each
time it runs. If none of this works for you, you can always construct
a client that reads directly from the API server, but this is generally
considered to be a last resort, and the two approaches above should
generally cover most circumstances.

### Q: Where's the fake client? How do I use it?

**A**: The fake client
[exists](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/client/fake),
but we generally recommend using
[envtest.Environment](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest#Environment)
to test against a real API server. In our experience, tests using fake
clients gradually re-implement poorly-written impressions of a real API
server, which leads to hard-to-maintain, complex test code.

### Q: How should I write tests? Any suggestions for getting started?

- Use the aforementioned
[envtest.Environment](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/envtest#Environment)
to spin up a real API server instead of trying to mock one out.

- Structure your tests to check that the state of the world is as you
expect it, *not* that a particular set of API calls were made, when
working with Kubernetes APIs. This will allow you to more easily
refactor and improve the internals of your controllers without changing
your tests.

- Remember that any time you're interacting with the API server, changes
may have some delay between write time and reconcile time.

### Q: What are these errors about no Kind being registered for a type?

**A**: You're probably missing a fully-set-up Scheme. Schemes record the
mapping between Go types and group-version-kinds in Kubernetes. In
general, your application should have its own Scheme containing the types
from the API groups that it needs (be they Kubernetes types or your own).
See the [scheme builder
docs](https://godoc.org/sigs.k8s.io/controller-runtime/pkg/scheme) for
more information.
6 changes: 5 additions & 1 deletion README.md
Expand Up @@ -21,7 +21,7 @@ Users:

- We follow [Semantic Versioning (semver)](https://semver.org)
- Use releases with your dependency management to ensure that you get compatible code
- The master branch contains all the latest code, some of which may break compatibility (so "normal" `go get` is not reccomended)
- The master branch contains all the latest code, some of which may break compatibility (so "normal" `go get` is not recommended)

Contributors:

Expand All @@ -34,6 +34,10 @@ Contributors:
* [Documentation Changes](/.github/PULL_REQUEST_TEMPLATE/docs.md)
* [Test/Build/Other Changes](/.github/PULL_REQUEST_TEMPLATE/other.md)

## FAQ

See [FAQ.md](FAQ.md)

## Community, discussion, contribution, and support

Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
Expand Down
2 changes: 1 addition & 1 deletion TMP-LOGGING.md
Expand Up @@ -74,7 +74,7 @@ logger.V(1).Info("this is particularly verbose!", "state of the world",
allKubernetesObjectsEverywhere)
```

While it's possible to use higher log levels, it's reccomended that you
While it's possible to use higher log levels, it's recommended that you
stick with `V(1)` or V(0)` (which is equivalent to not specifying `V`),
and then filter later based on key-value pairs or messages; different
numbers tend to lose meaning easily over time, and you'll be left
Expand Down
4 changes: 2 additions & 2 deletions VERSIONING.md
Expand Up @@ -10,7 +10,7 @@
- Use releases with your dependency management to ensure that you get
compatible code
- The master branch contains all the latest code, some of which may break
compatibility (so "normal" `go get` is not reccomended)
compatibility (so "normal" `go get` is not recommended)

### Contributors

Expand All @@ -32,7 +32,7 @@ Don't be lazy, read the rest of this doc :-)
## Overview

controller-runtime (and friends) follow [Semantic
Versioning](https://semver.org). I'd reccomend reading the aforementioned
Versioning](https://semver.org). I'd recommend reading the aforementioned
link if you're not familiar, but essentially, for any given release X.Y.Z:

- an X (*major*) release indicates a set of backwards-compatible code.
Expand Down
6 changes: 2 additions & 4 deletions alias.go
Expand Up @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package controllerruntime alias' common functions and types to improve discoverability and reduce
// the number of imports for simple Controllers.
package controllerruntime

import (
Expand All @@ -27,8 +25,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/runtime/scheme"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
"sigs.k8s.io/controller-runtime/pkg/scheme"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
)

// Builder builds an Application ControllerManagedBy (e.g. Operator) and returns a manager.Manager to start it.
Expand Down
124 changes: 124 additions & 0 deletions doc.go
@@ -0,0 +1,124 @@
/*
Copyright 2018 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 controllerruntime provides tools to construct Kubernetes-style
// controllers that manipulate both Kubernetes CRDs and aggregated/built-in
// Kubernetes APIs.
//
// It defines easy helpers for the common use cases when building CRDs, built
// on top of customizable layers of abstraction. Common cases should be easy,
// and uncommon cases should be possible. In general, controller-runtime tries
// to guide users towards Kubernetes controller best-practices.
//
// Getting Started
//
// The main entrypoint for controller-runtime is this root package, which
// contains all of the common types needed to get started building controllers:
// import (
// controllers "sigs.k8s.io/controller-runtime"
// )
//
// The examples in this package walk through a basic controller setup. The
// kubebuilder book (https://book.kubebuilder.io) has some more in-depth
// walkthroughs.
//
// controller-runtime favors structs with sane defaults over constructors, so
// it's fairly common to see structs being used directly in controller-runtime.
//
// Organization
//
// A brief-ish walkthrough of the layout of this library can be found below. Each
// package contains more information about how to use it.
//
// Frequently asked questions about using controller-runtime and designing
// controllers can be found at
// https://github.com/kubernetes-sigs/controller-runtime/blob/master/FAQ.
//
// Managers
//
// Every controller and webhook is ultimately run by a Manager (pkg/manager). A
// manager is responsible for running controllers and webhooks, and setting up
// common dependencies (pkg/runtime/inject), like shared caches and clients, as
// well as managing leader election (pkg/leaderelection). Managers are
// generally configured to gracefully shut down controllers on pod termination
// by wiring up a signal handler (pkg/manager/signals).
//
// Controllers
//
// Controllers (pkg/controller) use events (pkg/events) to eventually trigger
// reconcile requests. They may be constructed manually, but are often
// constructed with a Builder (pkg/builder), which eases the wiring of event
// sources (pkg/source), like Kubernetes API object changes, to event handlers
// (pkg/handler), like "enqueue a reconcile request for the object owner".
// Predicates (pkg/predicate) can be used to filter which events actually
// trigger reconciles. There are pre-written utilies for the common cases, and
// interfaces and helpers for advanced cases.
//
// Reconcilers
//
// Controller logic is implemented in terms of Reconcilers (pkg/reconcile). A
// Reconciler implements a function which takes a reconcile Request containing
// the name and namespace of the object to reconcile, reconciles the object,
// and returns a Response or an error indicating whether to requeue for a
// second round of processing.
//
// Clients and Caches
//
// Reconcilers use Clients (pkg/client) to access API objects. The default
// client provided by the manager reads from a local shared cache (pkg/cache)
// and writes directly to the API server, but clients can be constructed that
// only talk to the API server, without a cache. The Cache will auto-populate
// with watched objects, as well as when other structured objects are
// requested. Caches may also have indexes, which can be created via a
// FieldIndexer (pkg/client) obtained from the manager. Indexes can used to
// quickly and easily look up all objects with certain fields set. Reconcilers
// may retrieve event recorders (pkg/recorder) to emit events using the
// manager.
//
// Schemes
//
// Clients, Caches, and many other things in Kubernetes use Schemes
// (pkg/scheme) to associate Go types to Kubernetes API Kinds
// (Group-Version-Kinds, to be specific).
//
// Webhooks
//
// Similarly, webhooks (pkg/webhook/admission) may be implemented directly, but
// are often constructed using a builder (pkg/webhook/admission/builder). They
// are run via a server (pkg/webhook) which is managed by a Manager.
//
// Logging and Metrics
//
// Logging (pkg/log) in controller-runtime is done via structured logs, using a
// log set of interfaces called logr
// (https://godoc.org/github.com/go-logr/logr). While controller-runtime
// provides easy setup for using Zap (https://go.uber.org/zap, pkg/log/zap),
// you can provide any implementation of logr as the base logger for
// controller-runtime.
//
// Metrics (pkg/metrics) provided by controller-runtime are registered into a
// controller-runtime-specific Prometheus metrics registery. The manager can
// serve these by an HTTP endpoint, and additional metrics may be registered to
// this Registry as normal.
//
// Testing
//
// You can easily build integration and unit tests for your controllers and
// webhooks using the test Environment (pkg/envtest). This will automatically
// stand up a copy of etcd and kube-apiserver, and provide the correct options
// to connect to the API server. It's designed to work well with the Ginkgo
// testing framework, but should work with any testing setup.
package controllerruntime
2 changes: 1 addition & 1 deletion example/main.go
Expand Up @@ -31,7 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
"sigs.k8s.io/controller-runtime/pkg/source"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission/builder"
Expand Down
2 changes: 1 addition & 1 deletion pkg/builder/example_test.go
Expand Up @@ -30,7 +30,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/runtime/signals"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
)

// This example creates a simple application ControllerManagedBy that is configured for ReplicaSets and Pods.
Expand Down
25 changes: 12 additions & 13 deletions pkg/cache/cache.go
Expand Up @@ -34,17 +34,20 @@ import (

var log = logf.RuntimeLog.WithName("object-cache")

// Cache implements CacheReader by reading objects from a cache populated by InformersMap
// Cache knows how to load Kubernetes objects, fetch informers to request
// to receive events for Kubernetes objects (at a low-level),
// and add indicies to fields on the objects stored in the cache.
type Cache interface {
// Cache implements the client CacheReader
// Cache acts as a client to objects stored in the cache.
client.Reader

// Cache implements InformersMap
// Cache loads informers and adds field indicies.
Informers
}

// Informers knows how to create or fetch informers for different group-version-kinds.
// It's safe to call GetInformer from multiple threads.
// Informers knows how to create or fetch informers for different
// group-version-kinds, and add indicies to those informers. It's safe to call
// GetInformer from multiple threads.
type Informers interface {
// GetInformer fetches or constructs an informer for the given object that corresponds to a single
// API kind and resource.
Expand All @@ -61,15 +64,11 @@ type Informers interface {
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
WaitForCacheSync(stop <-chan struct{}) bool

// IndexField adds an index with the given field name on the given object type
// by using the given function to extract the value for that field. If you want
// compatibility with the Kubernetes API server, only return one key, and only use
// fields that the API server supports. Otherwise, you can return multiple keys,
// and "equality" in the field selector means that at least one key matches the value.
IndexField(obj runtime.Object, field string, extractValue client.IndexerFunc) error
// Informers knows how to add indicies to the caches (informers) that it manages.
client.FieldIndexer
}

// Options are the optional arguments for creating a new InformersMap object
// Options are the optional arguments for creating a new set of Informers.
type Options struct {
// Scheme is the scheme to use for mapping objects to GroupVersionKinds
Scheme *runtime.Scheme
Expand All @@ -87,7 +86,7 @@ type Options struct {

var defaultResyncTime = 10 * time.Hour

// New initializes and returns a new Cache
// New initializes and returns a new Cache.
func New(config *rest.Config, opts Options) (Cache, error) {
opts, err := defaultOpts(config, opts)
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions pkg/cache/doc.go
@@ -0,0 +1,19 @@
/*
Copyright 2019 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 cache provides object caches that act as caching client.Reader
// instances and help drive Kubernetes-object-based event handlers.
package cache
3 changes: 3 additions & 0 deletions pkg/client/apiutil/apimachinery.go
Expand Up @@ -14,6 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package apiutil contains utilities for working with raw Kubernetes
// API machinery, such as creating RESTMappers and raw REST clients,
// and extracting the GVK of an object.
package apiutil

import (
Expand Down
9 changes: 9 additions & 0 deletions pkg/client/client.go
Expand Up @@ -41,6 +41,15 @@ type Options struct {
}

// New returns a new Client using the provided config and Options.
// The returned client reads *and* writes directly from the server
// (it doesn't use object caches). It understands how to work with
// normal types (both custom resources and aggregated/built-in resources),
// as well as unstructured types.
//
// In the case of normal types, the scheme will be used to look up the
// corresponding group, version, and kind for the given type. In the
// case of unstrctured types, the group, version, and kind will be extracted
// from the corresponding fields on the object.
func New(config *rest.Config, options Options) (Client, error) {
if config == nil {
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/config/doc.go
Expand Up @@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package config contains libraries for initializing rest configs for talking to the Kubernetes API
// Package config contains libraries for initializing REST configs for talking to the Kubernetes API
package config

0 comments on commit 45d6520

Please sign in to comment.