docs snapshot for crossplane version `master`

This commit is contained in:
Crossplane 2020-01-22 17:26:53 +00:00
parent bad05f9706
commit f79a723c5d
1 changed files with 172 additions and 113 deletions

View File

@ -94,12 +94,12 @@ controller.
## Getting Started ## Getting Started
At the time of writing all Crossplane Services controllers are written in Go, At the time of writing all Crossplane Services controllers are written in Go,
and built using [kubebuilder] v0.2.x and [crossplane-runtime]. Per [What Makes and built using [kubebuilder] v0.2.x and [crossplane-runtime]. Per [What Makes a
a Crossplane Managed Service] it is possible to write a controller using any Crossplane Managed Service] it is possible to write a controller using any
language and tooling with a Kubernetes client, but this set of tools are the language and tooling with a Kubernetes client, but this set of tools are the
"[golden path]". They're well supported, broadly used, and provide a shared "[golden path]". They're well supported, broadly used, and provide a shared
language with the Crossplane maintainers. This guide targets [crossplane-runtime language with the Crossplane maintainers. This guide targets [crossplane-runtime
v0.2.1]. v0.4.0].
This guide assumes the reader is familiar with the Kubernetes [API Conventions] This guide assumes the reader is familiar with the Kubernetes [API Conventions]
and the [kubebuilder book]. If you're not adding a new managed service to an and the [kubebuilder book]. If you're not adding a new managed service to an
@ -127,8 +127,7 @@ kubebuilder create api \
--resource=true --controller=false --resource=true --controller=false
``` ```
The above command should produce a scaffold similar to the below The above command should produce a scaffold similar to the below example:
example:
```go ```go
type FancySQLInstanceSpec struct { type FancySQLInstanceSpec struct {
@ -161,8 +160,8 @@ the new resource kind is a managed resource, resource claim, or resource class.
The getters and setter methods required to satisfy the various The getters and setter methods required to satisfy the various
crossplane-runtime interfaces are omitted from the below examples for brevity. crossplane-runtime interfaces are omitted from the below examples for brevity.
They can be added by hand, but new services are encouraged to use [`angryjet`] They can be added by hand, but new services are encouraged to use [`angryjet`]
to generate them automatically using a `//go:generate` comment per the [`angryjet` to generate them automatically using a `//go:generate` comment per the
documentation]. [`angryjet` documentation].
Note that in many cases a suitable provider and resource claim will already Note that in many cases a suitable provider and resource claim will already
exist. Frequently adding support for a new managed service requires only the exist. Frequently adding support for a new managed service requires only the
@ -228,7 +227,7 @@ type FavouriteDBInstanceParameters struct {
// A FavouriteDBInstanceSpec defines the desired state of a FavouriteDBInstance. // A FavouriteDBInstanceSpec defines the desired state of a FavouriteDBInstance.
type FavouriteDBInstanceSpec struct { type FavouriteDBInstanceSpec struct {
runtimev1alpha1.ResourceSpec `json:",inline"` runtimev1alpha1.ResourceSpec `json:",inline"`
FavouriteDBInstanceParameters `json:",forProvider"` ForProvider FavouriteDBInstanceParameters `json:",forProvider"`
} }
// A FavouriteDBInstanceStatus represents the observed state of a // A FavouriteDBInstanceStatus represents the observed state of a
@ -257,7 +256,7 @@ type FavouriteDBInstance struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` metav1.ObjectMeta `json:"metadata,omitempty"`
Spec FavouriteDBInstanceSpec `json:"spec,omitempty"` Spec FavouriteDBInstanceSpec `json:"spec"`
Status FavouriteDBInstanceStatus `json:"status,omitempty"` Status FavouriteDBInstanceStatus `json:"status,omitempty"`
} }
``` ```
@ -276,20 +275,19 @@ defined in the same file as their the managed resource. Resource classes must:
* Satisfy crossplane-runtime's [`resource.Class`] interface. * Satisfy crossplane-runtime's [`resource.Class`] interface.
* Have a `SpecTemplate` struct field instead of a `Spec`. * Have a `SpecTemplate` struct field instead of a `Spec`.
* Embed a [`ClassSpecTemplate`] struct in their `SpecTemplate` struct. * Embed a [`ClassSpecTemplate`] struct in their `SpecTemplate` struct.
* Embed their managed resource's `Parameters` struct in their `SpecTemplate` * Embed their managed resource's `Parameters` struct as `ForProvider` in their
struct. `SpecTemplate` struct.
* Not have a `Status` struct. * Not have a `Status` struct.
* Use the `+kubebuilder:resource:scope=Cluster` [comment marker]. * Use the `+kubebuilder:resource:scope=Cluster` [comment marker].
A resource class for the above `FavouriteDBInstance` would look as A resource class for the above `FavouriteDBInstance` would look as follows:
follows:
```go ```go
// A FavouriteDBInstanceClassSpecTemplate is a template for the spec of a // A FavouriteDBInstanceClassSpecTemplate is a template for the spec of a
// dynamically provisioned FavouriteDBInstance. // dynamically provisioned FavouriteDBInstance.
type FavouriteDBInstanceClassSpecTemplate struct { type FavouriteDBInstanceClassSpecTemplate struct {
runtimev1alpha1.ClassSpecTemplate `json:",inline"` runtimev1alpha1.ClassSpecTemplate `json:",inline"`
FavouriteDBInstanceParameters `json:",forProvider"` ForProvider FavouriteDBInstanceParameters `json:",forProvider"`
} }
// A FavouriteDBInstanceClass is a resource class. It defines the desired spec // A FavouriteDBInstanceClass is a resource class. It defines the desired spec
@ -372,8 +370,7 @@ infrastructure stack that adds support for a new infrastructure provider.
Providers must: Providers must:
* Be named exactly `Provider`. * Be named exactly `Provider`.
* Have a `Spec` struct with a `Secret` field indicating where to find * Embed a [`ProviderSpec`] struct in their `Spec` struct.
credentials for this provider.
* Use the `+kubebuilder:resource:scope=Cluster` [comment marker]. * Use the `+kubebuilder:resource:scope=Cluster` [comment marker].
The Favourite Cloud `Provider` would look as follows. Note that the cloud to The Favourite Cloud `Provider` would look as follows. Note that the cloud to
@ -383,10 +380,12 @@ would be `favouritecloud.crossplane.io/v1alpha1` or similar.
```go ```go
// A ProviderSpec defines the desired state of a Provider. // A ProviderSpec defines the desired state of a Provider.
type ProviderSpec struct { type ProviderSpec struct {
runtimev1alpha1.ProviderSpec `json:",inline"`
// A Secret containing credentials for a Favourite Cloud Service Account // Information required outside of the Secret referenced in the embedded
// that will be used to authenticate to this Provider. // runtimev1alpha1.ProviderSpec that is required to authenticate to the provider.
Secret runtimev1alpha1.SecretKeySelector `json:"credentialsSecretRef"` // ProjectID is used as an example here.
ProjectID string `json:"projectID"`
} }
// A Provider configures a Favourite Cloud 'provider', i.e. a connection to a // A Provider configures a Favourite Cloud 'provider', i.e. a connection to a
@ -396,7 +395,7 @@ type Provider struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ProviderSpec `json:"spec,omitempty"` Spec ProviderSpec `json:"spec"`
} }
``` ```
@ -412,7 +411,8 @@ claim. Before moving on to the controllers:
* Run `make generate && make manifests` (or `make reviewable` if you're working * Run `make generate && make manifests` (or `make reviewable` if you're working
in one of the projects in the [crossplaneio org]) to generate Custom Resource in one of the projects in the [crossplaneio org]) to generate Custom Resource
Definitions and additional helper methods for your new resource kinds. Definitions and additional helper methods for your new resource kinds.
* Make sure a `//go:generate` comment exists for [angryjet] and you ran `go generate -v ./...` * Make sure a `//go:generate` comment exists for [angryjet] and you ran `go
generate -v ./...`
* Make sure any package documentation (i.e. `// Package v1alpha1...` GoDoc, * Make sure any package documentation (i.e. `// Package v1alpha1...` GoDoc,
including package level comment markers) are in a file named `doc.go`. including package level comment markers) are in a file named `doc.go`.
kubebuilder adds them to `groupversion_info.go`, but several code generation kubebuilder adds them to `groupversion_info.go`, but several code generation
@ -450,21 +450,25 @@ Crossplane-specific tasks.
crossplane-runtime provides the following `reconcile.Reconcilers`: crossplane-runtime provides the following `reconcile.Reconcilers`:
* The [`resource.ManagedReconciler`] reconciles managed resources with external * The [`managed.Reconciler`] reconciles managed resources with external systems
systems by instantiating a client of the external API and using it to create, by instantiating a client of the external API and using it to create, update,
update, or delete the external resource as necessary. or delete the external resource as necessary.
* [`resource.ClaimSchedulingReconciler`] reconciles resource claims by * [`claimscheduling.Reconciler`] reconciles resource claims by scheduling them
scheduling them to a resource class that matches their class selector labels to a resource class that matches their class selector labels (if any).
* [`claimdefaulting.Reconciler`] reconciles resource claims that omit their
class selector by defaulting them to a resource class annotated as the default
(if any). (if any).
* [`resource.ClaimDefaultingReconciler`] reconciles resource claims that omit * [`claimbinding.Reconciler`] reconciles resource claims with managed resources
their class selector by defaulting them to a resource class annotated as the
default (if any).
* [`resource.ClaimReconciler`] reconciles resource claims with managed resources
by either binding or dynamically provisioning and then binding them. by either binding or dynamically provisioning and then binding them.
* [`resource.SecretPropagatingReconciler`] reconciles secrets by propagating * [`secret.NewReconciler`] reconciles secrets by propagating their data to
their data to another secret. This controller is typically used to ensure another secret. This controller is typically used to ensure resource claim
resource claim connection secrets remain in sync with the connection secrets connection secrets remain in sync with the connection secrets of their bound
of their bound managed resources. managed resources.
* [`target.Reconciler`] reconciles `KubernetesTarget` resources that reference
managed resources that provide a hosted Kubernetes service (i.e. GKE, EKS,
AKS). This controller is used to propagate the connection information of the
referenced Kubernetes cluster to the namespace of the `KubernetesTarget` in
the form of a secret.
Crossplane controllers typically differ sufficiently from those scaffolded by Crossplane controllers typically differ sufficiently from those scaffolded by
kubebuilder that there is little value in using kubebuilder to generate a kubebuilder that there is little value in using kubebuilder to generate a
@ -472,12 +476,12 @@ controller scaffold.
### Managed Resource Controllers ### Managed Resource Controllers
Managed resource controllers should use [`resource.NewManagedReconciler`] to Managed resource controllers should use [`managed.NewReconciler`] to wrap a
wrap a managed-resource specific implementation of managed-resource specific implementation of [`managed.ExternalConnecter`]. Parts
[`resource.ExternalConnecter`]. Parts of `resource.ManagedReconciler`'s of `managed.Reconciler`'s behaviour is customisable; refer to the
behaviour is customisable; refer to the [`resource.NewManagedReconciler`] GoDoc [`managed.NewReconciler`] GoDoc for a list of options. The following is an
for a list of options. The following is an example controller for the example controller for the `FavouriteDBInstance` managed resource we defined
`FavouriteDBInstance` managed resource we defined earlier: earlier:
```go ```go
import ( import (
@ -497,6 +501,7 @@ import (
runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1" runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplaneio/crossplane-runtime/pkg/meta" "github.com/crossplaneio/crossplane-runtime/pkg/meta"
"github.com/crossplaneio/crossplane-runtime/pkg/resource" "github.com/crossplaneio/crossplane-runtime/pkg/resource"
"github.com/crossplaneio/crossplane-runtime/pkg/reconciler/managed"
"github.com/crossplaneio/stack-fcp/apis/database/v1alpha3" "github.com/crossplaneio/stack-fcp/apis/database/v1alpha3"
fcpv1alpha3 "github.com/crossplaneio/stack-fcp/apis/v1alpha3" fcpv1alpha3 "github.com/crossplaneio/stack-fcp/apis/v1alpha3"
@ -504,16 +509,16 @@ import (
type FavouriteDBInstanceController struct{} type FavouriteDBInstanceController struct{}
// SetupWithManager instantiates a new controller using a resource.ManagedReconciler // SetupWithManager instantiates a new controller using a managed.Reconciler
// configured to reconcile FavouriteDBInstances using an ExternalClient produced by // configured to reconcile FavouriteDBInstances using an ExternalClient produced by
// connecter, which satisfies the ExternalConnecter interface. // connecter, which satisfies the ExternalConnecter interface.
func (c *FavouriteDBInstanceController) SetupWithManager(mgr ctrl.Manager) error { func (c *FavouriteDBInstanceController) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
Named(strings.ToLower(fmt.Sprintf("%s.%s", v1alpha3.FavouriteDBInstanceKind, v1alpha3.Group))). Named(strings.ToLower(fmt.Sprintf("%s.%s", v1alpha3.FavouriteDBInstanceKind, v1alpha3.Group))).
For(&v1alpha3.FavouriteDBInstance{}). For(&v1alpha3.FavouriteDBInstance{}).
Complete(resource.NewManagedReconciler(mgr, Complete(managed.NewReconciler(mgr,
resource.ManagedKind(v1alpha3.FavouriteDBInstanceGroupVersionKind), resource.ManagedKind(v1alpha3.FavouriteDBInstanceGroupVersionKind),
resource.WithExternalConnecter(&connecter{client: mgr.GetClient()}))) managed.WithExternalConnecter(&connecter{client: mgr.GetClient()})))
} }
// Connecter satisfies the resource.ExternalConnecter interface. // Connecter satisfies the resource.ExternalConnecter interface.
@ -522,7 +527,7 @@ type connecter struct{ client client.Client }
// Connect to the supplied resource.Managed (presumed to be a // Connect to the supplied resource.Managed (presumed to be a
// FavouriteDBInstance) by using the Provider it references to create a new // FavouriteDBInstance) by using the Provider it references to create a new
// database client. // database client.
func (c *connecter) Connect(ctx context.Context, mg resource.Managed) (resource.ExternalClient, error) { func (c *connecter) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) {
// Assert that resource.Managed we were passed in fact contains a // Assert that resource.Managed we were passed in fact contains a
// FavouriteDBInstance. We told NewControllerManagedBy that this was a // FavouriteDBInstance. We told NewControllerManagedBy that this was a
// controller For FavouriteDBInstance, so something would have to go // controller For FavouriteDBInstance, so something would have to go
@ -554,13 +559,13 @@ func (c *connecter) Connect(ctx context.Context, mg resource.Managed) (resource.
// External satisfies the resource.ExternalClient interface. // External satisfies the resource.ExternalClient interface.
type external struct{ client database.Client } type external struct{ client database.Client }
// Observe the existing external resource, if any. The resource.ManagedReconciler // Observe the existing external resource, if any. The managed.Reconciler
// calls Observe in order to determine whether an external resource needs to be // calls Observe in order to determine whether an external resource needs to be
// created, updated, or deleted. // created, updated, or deleted.
func (e *external) Observe(ctx context.Context, mg resource.Managed) (resource.ExternalObservation, error) { func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) {
i, ok := mg.(*v1alpha3.FavouriteDBInstance) i, ok := mg.(*v1alpha3.FavouriteDBInstance)
if !ok { if !ok {
return resource.ExternalObservation{}, errors.New("managed resource is not a FavouriteDBInstance") return managed.ExternalObservation{}, errors.New("managed resource is not a FavouriteDBInstance")
} }
// Use our FavouriteDB API client to get an up to date view of the external // Use our FavouriteDB API client to get an up to date view of the external
@ -568,17 +573,17 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (resource.E
existing, err := e.client.GetInstance(ctx, i.Spec.Name) existing, err := e.client.GetInstance(ctx, i.Spec.Name)
// If we encounter an error indicating the external resource does not exist // If we encounter an error indicating the external resource does not exist
// we want to let the resource.ManagedReconciler know so it can create it. // we want to let the managed.Reconciler know so it can create it.
if database.IsNotFound(err) { if database.IsNotFound(err) {
return resource.ExternalObservation{ResourceExists: false}, nil return managed.ExternalObservation{ResourceExists: false}, nil
} }
// Any other errors are wrapped (as is good Go practice) and returned to the // Any other errors are wrapped (as is good Go practice) and returned to the
// resource.ManagedReconciler. It will update the "Synced" status condition // managed.Reconciler. It will update the "Synced" status condition
// of the managed resource to reflect that the most recent reconcile failed // of the managed resource to reflect that the most recent reconcile failed
// and ensure the reconcile is reattempted after a brief wait. // and ensure the reconcile is reattempted after a brief wait.
if err != nil { if err != nil {
return resource.ExternalObservation{}, errors.Wrap(err, "cannot get instance") return managed.ExternalObservation{}, errors.Wrap(err, "cannot get instance")
} }
// The external resource exists. Copy any output-only fields to their // The external resource exists. Copy any output-only fields to their
@ -609,10 +614,10 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (resource.E
// the actual fanciness level matches our desired fanciness level. Any // the actual fanciness level matches our desired fanciness level. Any
// ConnectionDetails we return will be published to the managed resource's // ConnectionDetails we return will be published to the managed resource's
// connection secret if it specified one. // connection secret if it specified one.
o := resource.ExternalObservation{ o := managed.ExternalObservation{
ResourceExists: true, ResourceExists: true,
ResourceUpToDate: existing.GetFancinessLevel == i.Spec.FancinessLevel, ResourceUpToDate: existing.GetFancinessLevel == i.Spec.FancinessLevel,
ConnectionDetails: resource.ConnectionDetails{ ConnectionDetails: managed.ConnectionDetails{
runtimev1alpha1.ResourceCredentialsSecretUserKey: []byte(existing.GetUsername()), runtimev1alpha1.ResourceCredentialsSecretUserKey: []byte(existing.GetUsername()),
runtimev1alpha1.ResourceCredentialsSecretEndpointKey: []byte(existing.GetHostname()), runtimev1alpha1.ResourceCredentialsSecretEndpointKey: []byte(existing.GetHostname()),
}, },
@ -622,12 +627,12 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (resource.E
} }
// Create a new external resource based on the specification of our managed // Create a new external resource based on the specification of our managed
// resource. resource.ManagedReconciler only calls Create if Observe reported // resource. managed.Reconciler only calls Create if Observe reported
// that the external resource did not exist. // that the external resource did not exist.
func (e *external) Create(ctx context.Context, mg resource.Managed) (resource.ExternalCreation, error) { func (e *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) {
i, ok := mg.(*v1alpha3.FavouriteDBInstance) i, ok := mg.(*v1alpha3.FavouriteDBInstance)
if !ok { if !ok {
return resource.ExternalCreation{}, errors.New("managed resource is not a FavouriteDBInstance") return managed.ExternalCreation{}, errors.New("managed resource is not a FavouriteDBInstance")
} }
// Indicate that we're about to create the instance. Remember ExternalClient // Indicate that we're about to create the instance. Remember ExternalClient
// authors can use a bespoke condition reason here in cases where Creating // authors can use a bespoke condition reason here in cases where Creating
@ -635,10 +640,10 @@ func (e *external) Create(ctx context.Context, mg resource.Managed) (resource.Ex
i.SetConditions(runtimev1alpha1.Creating()) i.SetConditions(runtimev1alpha1.Creating())
// Create must return any connection details that are set or returned only // Create must return any connection details that are set or returned only
// at creation time. The resource.ManagedReconciler will merge any details // at creation time. The managed.Reconciler will merge any details
// with those returned during the Observe phase. // with those returned during the Observe phase.
password := database.GeneratePassword() password := database.GeneratePassword()
cd := resource.ConnectionDetails{runtimev1alpha1.ResourceCredentialsSecretPasswordKey: []byte(password)} cd := managed.ConnectionDetails{runtimev1alpha1.ResourceCredentialsSecretPasswordKey: []byte(password)}
// Create a new instance. // Create a new instance.
new := database.Instance{Name: i.Name, FancinessLevel: i.FancinessLevel, Version: i.Version} new := database.Instance{Name: i.Name, FancinessLevel: i.FancinessLevel, Version: i.Version}
@ -650,25 +655,25 @@ func (e *external) Create(ctx context.Context, mg resource.Managed) (resource.Ex
// resource controllers are advised to avoid unintentially 'adoptign' an // resource controllers are advised to avoid unintentially 'adoptign' an
// existing, unrelated external resource, per // existing, unrelated external resource, per
// https://github.com/crossplaneio/crossplane-runtime/issues/27 // https://github.com/crossplaneio/crossplane-runtime/issues/27
return resource.ExternalCreation{ConnectionDetails: cd}, errors.Wrap(resource.Ignore(database.IsExists, err), "cannot create instance") return managed.ExternalCreation{ConnectionDetails: cd}, errors.Wrap(resource.Ignore(database.IsExists, err), "cannot create instance")
} }
// Update the existing external resource to match the specifications of our // Update the existing external resource to match the specifications of our
// managed resource. resource.ManagedReconciler only calls Update if Observe // managed resource. managed.Reconciler only calls Update if Observe
// reported that the external resource was not up to date. // reported that the external resource was not up to date.
func (e *external) Update(ctx context.Context, mg resource.Managed) (resource.ExternalUpdate, error) { func (e *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) {
i, ok := mg.(*v1alpha3.FavouriteDBInstance) i, ok := mg.(*v1alpha3.FavouriteDBInstance)
if !ok { if !ok {
return resource.ExternalUpdate{}, errors.New("managed resource is not a FavouriteDBInstance") return managed.ExternalUpdate{}, errors.New("managed resource is not a FavouriteDBInstance")
} }
// Recall that FancinessLevel is the only field that we _can_ update. // Recall that FancinessLevel is the only field that we _can_ update.
new := database.Instance{Name: i.Name, FancinessLevel: i.FancinessLevel} new := database.Instance{Name: i.Name, FancinessLevel: i.FancinessLevel}
err := e.client.UpdateInstance(ctx, new) err := e.client.UpdateInstance(ctx, new)
return resource.ExternalUpdate{}, errors.Wrap(err, "cannot update instance") return managed.ExternalUpdate{}, errors.Wrap(err, "cannot update instance")
} }
// Delete the external resource. resource.ManagedReconciler only calls Delete // Delete the external resource. managed.Reconciler only calls Delete
// when a managed resource with the 'Delete' reclaim policy has been deleted. // when a managed resource with the 'Delete' reclaim policy has been deleted.
func (e *external) Delete(ctx context.Context, mg resource.Managed) error { func (e *external) Delete(ctx context.Context, mg resource.Managed) error {
i, ok := mg.(*v1alpha3.FavouriteDBInstance) i, ok := mg.(*v1alpha3.FavouriteDBInstance)
@ -690,13 +695,13 @@ func (e *external) Delete(ctx context.Context, mg resource.Managed) error {
### Resource Claim Scheduling Controllers ### Resource Claim Scheduling Controllers
Scheduling controllers should use [`resource.NewClaimSchedulingReconciler`] to Scheduling controllers should use [`claimscheduling.NewReconciler`] to specify
specify the resource claim kind it schedules and the resource class kind it the resource claim kind it schedules and the resource class kind it schedules
schedules them to. Note that unlike their resource claim kinds, resource claim them to. Note that unlike their resource claim kinds, resource claim scheduling
scheduling controllers are always part of the infrastructure stack that defines controllers are always part of the infrastructure stack that defines the
the resource class they schedule claims to. The following is an example resource class they schedule claims to. The following is an example controller
controller that reconciles the `FancySQLInstance` resource claim by scheduling that reconciles the `FancySQLInstance` resource claim by scheduling it to a
it to a `FavouriteDBInstanceClass`: `FavouriteDBInstanceClass`:
```go ```go
import ( import (
@ -707,6 +712,7 @@ import (
runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1" runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplaneio/crossplane-runtime/pkg/resource" "github.com/crossplaneio/crossplane-runtime/pkg/resource"
"github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimscheduling"
// Note that the hypothetical FancySQL resource claim is part of Crossplane, // Note that the hypothetical FancySQL resource claim is part of Crossplane,
// not stack-fcp, because it is (hypothetically) portable across multiple // not stack-fcp, because it is (hypothetically) portable across multiple
@ -748,7 +754,7 @@ func (c *FancySQLInstanceClaimSchedulingController) SetupWithManager(mgr ctrl.Ma
// existing managed resource. // existing managed resource.
resource.HasNoManagedResourceReference(), resource.HasNoManagedResourceReference(),
))). ))).
Complete(resource.NewClaimSchedulingReconciler(mgr, Complete(claimscheduling.NewReconciler(mgr,
resource.ClaimKind(databasev1alpha1.FancySQLInstanceGroupVersionKind), resource.ClaimKind(databasev1alpha1.FancySQLInstanceGroupVersionKind),
resource.ClassKind(v1alpha3.FavouriteDBInstanceClassGroupVersionKind), resource.ClassKind(v1alpha3.FavouriteDBInstanceClassGroupVersionKind),
)) ))
@ -758,13 +764,13 @@ func (c *FancySQLInstanceClaimSchedulingController) SetupWithManager(mgr ctrl.Ma
### Resource Claim Defaulting Controllers ### Resource Claim Defaulting Controllers
Defaulting controllers are configured almost (but not quite) identically to Defaulting controllers are configured almost (but not quite) identically to
scheduling controllers. They use a [`resource.NewClaimSchedulingReconciler`] to scheduling controllers. They use a [`claimdefaulting.NewReconciler`] to specify
specify the resource claim kind they configure and the resource class kind they the resource claim kind they configure and the resource class kind they default
default to. Unlike their resource claim kinds, defaulting controllers are always to. Unlike their resource claim kinds, defaulting controllers are always part of
part of the infrastructure stack that defines the resource class they default the infrastructure stack that defines the resource class they default claims to.
claims to. The following is an example controller that reconciles the The following is an example controller that reconciles the `FancySQLInstance`
`FancySQLInstance` resource claim by setting its class reference to a resource claim by setting its class reference to a `FavouriteDBInstanceClass`
`FavouriteDBInstanceClass` annotated as the default class: annotated as the default class:
```go ```go
import ( import (
@ -775,6 +781,7 @@ import (
runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1" runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplaneio/crossplane-runtime/pkg/resource" "github.com/crossplaneio/crossplane-runtime/pkg/resource"
"github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimdefaulting"
// Note that the hypothetical FancySQL resource claim is part of Crossplane, // Note that the hypothetical FancySQL resource claim is part of Crossplane,
// not stack-fcp, because it is (hypothetically) portable across multiple // not stack-fcp, because it is (hypothetically) portable across multiple
@ -815,7 +822,7 @@ func (c *FancySQLInstanceClaimDefaultingController) SetupWithManager(mgr ctrl.Ma
// existing managed resource. // existing managed resource.
resource.HasNoManagedResourceReference(), resource.HasNoManagedResourceReference(),
))). ))).
Complete(resource.NewClaimDefaultingReconciler(mgr, Complete(claimdefaulting.NewReconciler(mgr,
resource.ClaimKind(databasev1alpha1.FancySQLInstanceGroupVersionKind), resource.ClaimKind(databasev1alpha1.FancySQLInstanceGroupVersionKind),
resource.ClassKind(v1alpha3.FavouriteDBInstanceClassGroupVersionKind), resource.ClassKind(v1alpha3.FavouriteDBInstanceClassGroupVersionKind),
)) ))
@ -824,15 +831,15 @@ func (c *FancySQLInstanceClaimDefaultingController) SetupWithManager(mgr ctrl.Ma
### Resource Claim Controllers ### Resource Claim Controllers
Resource claim controllers should use [`resource.NewClaimReconciler`] to wrap a Resource claim controllers should use [`claimbinding.NewReconciler`] to wrap a
managed-resource specific implementation of [`resource.ManagedConfigurator`]. managed-resource specific implementation of
Parts of `resource.ClaimReconciler`'s behaviour is customisable; refer to the [`claimbinding.ManagedConfigurator`]. Parts of `claimbinding.Reconciler`'s
[`resource.NewClaimReconciler`] GoDoc for a list of options. Note that unlike behaviour is customisable; refer to the [`claimbinding.NewReconciler`] GoDoc for
their resource claim kinds, resource claim controllers are always part of the a list of options. Note that unlike their resource claim kinds, resource claim
infrastructure stack that defines the managed resource they reconcile claims controllers are always part of the infrastructure stack that defines the managed
with. The following is an example controller that reconciles the resource they reconcile claims with. The following is an example controller that
`FancySQLInstance` resource claim with the `FavouriteDBInstance` managed reconciles the `FancySQLInstance` resource claim with the `FavouriteDBInstance`
resource: managed resource:
```go ```go
import ( import (
@ -847,6 +854,7 @@ import (
runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1" runtimev1alpha1 "github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplaneio/crossplane-runtime/pkg/resource" "github.com/crossplaneio/crossplane-runtime/pkg/resource"
"github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimbinding"
// Note that the hypothetical FancySQL resource claim is part of Crossplane, // Note that the hypothetical FancySQL resource claim is part of Crossplane,
// not stack-fcp, because it is (hypothetically) portable across multiple // not stack-fcp, because it is (hypothetically) portable across multiple
@ -889,16 +897,16 @@ func (c *FavouriteDBInstanceClaimController) SetupWithManager(mgr ctrl.Manager)
)) ))
// Create a new resource claim reconciler... // Create a new resource claim reconciler...
r := resource.NewClaimReconciler(mgr, r := claimbinding.NewReconciler(mgr,
// ..that uses the supplied claim, class, and managed resource kinds. // ..that uses the supplied claim, class, and managed resource kinds.
resource.ClaimKind(databasev1alpha1.FancySQLInstanceGroupVersionKind), resource.ClaimKind(databasev1alpha1.FancySQLInstanceGroupVersionKind),
resource.ClassKind(v1alpha3.FavouriteDBInstanceClassGroupVersionKind), resource.ClassKind(v1alpha3.FavouriteDBInstanceClassGroupVersionKind),
resource.ManagedKind(v1alpha3.FavouriteDBInstanceGroupVersionKind), resource.ManagedKind(v1alpha3.FavouriteDBInstanceGroupVersionKind),
// The following configurators configure how a managed resource will be // The following configurators configure how a managed resource will be
// configured when one must be dynamically provisioned. // configured when one must be dynamically provisioned.
resource.WithManagedConfigurators( claimbinding.WithManagedConfigurators(
resource.ManagedConfiguratorFn(ConfigureFavouriteDBInstance), claimbinding.ManagedConfiguratorFn(ConfigureFavouriteDBInstance),
resource.NewObjectMetaConfigurator(mgr.GetScheme()), claimbinding.NewObjectMetaConfigurator(mgr.GetScheme()),
)) ))
// Note that we watch for both FancySQLInstance and FavouriteDBInstance // Note that we watch for both FancySQLInstance and FavouriteDBInstance
@ -973,6 +981,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source" "sigs.k8s.io/controller-runtime/pkg/source"
"github.com/crossplaneio/crossplane-runtime/pkg/resource" "github.com/crossplaneio/crossplane-runtime/pkg/resource"
"github.com/crossplaneio/crossplane-runtime/pkg/reconciler/secret"
databasev1alpha1 "github.com/crossplaneio/crossplane/apis/database/v1alpha1" databasev1alpha1 "github.com/crossplaneio/crossplane/apis/database/v1alpha1"
"github.com/crossplaneio/stack-fcp/apis/database/v1alpha3" "github.com/crossplaneio/stack-fcp/apis/database/v1alpha3"
@ -988,10 +997,59 @@ func (c *FavouriteDBInstanceSecretController) SetupWithManager(mgr ctrl.Manager)
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
Named(strings.ToLower(fmt.Sprintf("connectionsecret.%s.%s", v1alpha3.FavouriteDBInstanceKind, v1alpha3.Group))). Named(strings.ToLower(fmt.Sprintf("connectionsecret.%s.%s", v1alpha3.FavouriteDBInstanceKind, v1alpha3.Group))).
Watches(&source.Kind{Type: &corev1.Secret{}}, &resource.EnqueueRequestForPropagator{}). Watches(&source.Kind{Type: &corev1.Secret{}}, &resource.EnqueueRequestForPropagated{}).
For(&corev1.Secret{}). For(&corev1.Secret{}).
WithEventFilter(p). WithEventFilter(p).
Complete(resource.NewSecretPropagatingReconciler(mgr)) Complete(secret.NewReconciler(mgr))
}
```
### Target Controller
Managed resources that represent a hosted Kubernetes cluster can be referenced
by `KubernetesTarget` resources in a namespace where `KubernetesApplication`
resources want to be created and scheduled to the remote cluster. This
controller evaluates if a newly created `KubernetesTarget` references its hosted
Kubernetes cluster managed resource, and if so, propagates its connection
information to the namespace of the `KubernetesTarget`. It will also set
annotations on the propagated secret in case there is a connection secret
controller that is set to continously propagate the connection information as it
changes.
The following controller propagates the connection secret of a
`FavouriteCluster` to the namespace of a `KubernetesTarget` that references it.
Note that `FavouriteCluster` is used instead of `FavouriteDBInstance` due to the
fact that Target controllers are currently only utilized for managed resources
that represent a hosted Kubernetes cluster offering.
```go
import (
"fmt"
"strings"
ctrl "sigs.k8s.io/controller-runtime"
"github.com/crossplaneio/crossplane-runtime/pkg/reconciler/target"
"github.com/crossplaneio/crossplane-runtime/pkg/resource"
workloadv1alpha1 "github.com/crossplaneio/crossplane/apis/workload/v1alpha1"
"github.com/crossplaneio/stack-fcp/apis/compute/v1alpha3"
)
type FavoriteClusterTargetController struct{}
func (c *FavouriteClusterTargetController) SetupWithManager(mgr ctrl.Manager) error {
p := resource.NewPredicates(resource.HasManagedResourceReferenceKind(resource.ManagedKind(v1alpha3.FavouriteClusterGroupVersionKind)))
r := target.NewReconciler(mgr,
resource.TargetKind(workloadv1alpha1.KubernetesTargetGroupVersionKind),
resource.ManagedKind(v1alpha3.FavouriteClusterGroupVersionKind))
return ctrl.NewControllerManagedBy(mgr).
Named(strings.ToLower(fmt.Sprintf("kubernetestarget.%s.%s", v1alpha3.FavouriteClusterKind, v1alpha3.Group))).
For(&workloadv1alpha1.KubernetesTarget{}).
WithEventFilter(p).
Complete(r)
} }
``` ```
@ -1063,16 +1121,16 @@ value any feedback you may have about the services development process!
[resource claim]: concepts.md#resource-claim [resource claim]: concepts.md#resource-claim
[resource class]: concepts.md#resource-class [resource class]: concepts.md#resource-class
[dynamic provisioning]: concepts.md#dynamic-and-static-provisioning [dynamic provisioning]: concepts.md#dynamic-and-static-provisioning
[`CloudMemorystoreInstance`]: https://github.com/crossplaneio/stack-gcp/blob/42ebb8b71/gcp/apis/cache/v1beta1/cloudmemorystore_instance_types.go#L146 [`CloudMemorystoreInstance`]: https://github.com/crossplaneio/stack-gcp/blob/85a6ed3c669a021f1d61be51b2cbe2714b0bc70b/apis/cache/v1beta1/cloudmemorystore_instance_types.go#L184
[`CloudMemorystoreInstanceClass`]: https://github.com/crossplaneio/stack-gcp/blob/42ebb8b71/gcp/apis/cache/v1beta1/cloudmemorystore_instance_types.go#L237 [`CloudMemorystoreInstanceClass`]: https://github.com/crossplaneio/stack-gcp/blob/85a6ed3c669a021f1d61be51b2cbe2714b0bc70b/apis/cache/v1beta1/cloudmemorystore_instance_types.go#L217
[`Provider`]: https://github.com/crossplaneio/stack-gcp/blob/24ab7381b/gcp/apis/v1alpha3/types.go#L37 [`Provider`]: https://github.com/crossplaneio/stack-gcp/blob/85a6ed3c669a021f1d61be51b2cbe2714b0bc70b/apis/v1alpha3/types.go#L41
[`RedisCluster`]: https://github.com/crossplaneio/crossplane/blob/3c6cf4e/apis/cache/v1alpha1/rediscluster_types.go#L40 [`RedisCluster`]: https://github.com/crossplaneio/crossplane/blob/3c6cf4e/apis/cache/v1alpha1/rediscluster_types.go#L40
[`RedisClusterClass`]: https://github.com/crossplaneio/crossplane/blob/3c6cf4e/apis/cache/v1alpha1/rediscluster_types.go#L116 [`RedisClusterClass`]: https://github.com/crossplaneio/crossplane/blob/3c6cf4e/apis/cache/v1alpha1/rediscluster_types.go#L116
[watching the API server]: https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes [watching the API server]: https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes
[kubebuilder]: https://kubebuilder.io/ [kubebuilder]: https://kubebuilder.io/
[controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime [controller-runtime]: https://github.com/kubernetes-sigs/controller-runtime
[crossplane-runtime]: https://github.com/crossplaneio/crossplane-runtime/ [crossplane-runtime]: https://github.com/crossplaneio/crossplane-runtime/
[crossplane-runtime v0.2.1]: https://github.com/crossplaneio/crossplane-runtime/releases/tag/v0.2.1 [crossplane-runtime v0.4.0]: https://github.com/crossplaneio/crossplane-runtime/releases/tag/v0.4.0
[golden path]: https://charity.wtf/2018/12/02/software-sprawl-the-golden-path-and-scaling-teams-with-agency/ [golden path]: https://charity.wtf/2018/12/02/software-sprawl-the-golden-path-and-scaling-teams-with-agency/
[API Conventions]: https://github.com/kubernetes/community/blob/c6e1e89a/contributors/devel/sig-architecture/api-conventions.md [API Conventions]: https://github.com/kubernetes/community/blob/c6e1e89a/contributors/devel/sig-architecture/api-conventions.md
[kubebuilder book]: https://book.kubebuilder.io/ [kubebuilder book]: https://book.kubebuilder.io/
@ -1085,25 +1143,26 @@ value any feedback you may have about the services development process!
[`resource.Managed`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#Managed [`resource.Managed`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#Managed
[`resource.Claim`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#Claim [`resource.Claim`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#Claim
[`resource.Class`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#Class [`resource.Class`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#Class
[`resource.ManagedReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ManagedReconciler [`managed.Reconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/managed#Reconciler
[`resource.NewManagedReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#NewManagedReconciler [`managed.NewReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/managed#NewReconciler
[`resource.ClaimReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ClaimReconciler [`claimbinding.Reconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimbinding#Reconciler
[`resource.NewClaimReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#NewClaimReconciler [`claimbinding.NewReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimbinding#NewReconciler
[`resource.ClaimSchedulingReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ClaimSchedulingReconciler [`claimscheduling.Reconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimscheduling#Reconciler
[`resource.NewClaimSchedulingReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#NewClaimSchedulingReconciler [`claimscheduling.NewReconciler`]: https://github.com/crossplaneio/crossplane-runtime/blob/master/pkg/reconciler/claimscheduling/reconciler.go#L83
[`resource.ClaimDefaultingReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ClaimDefaultingReconciler [`claimdefaulting.Reconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimdefaulting#Reconciler
[`resource.NewClaimDefaultingReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#NewClaimDefaultingReconciler [`claimdefaulting.NewReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimdefaulting#NewReconciler
[`resource.SecretPropagatingReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#SecretPropagatingReconciler [`secret.NewReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/secret#NewReconciler
[`resource.NewSecretPropagatingReconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#NewSecretPropagatingReconciler [`managed.ExternalConnecter`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/managed#ExternalConnecter
[`resource.ExternalConnecter`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ExternalConnecter [`managed.ExternalClient`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/managed#ExternalClient
[`resource.ExternalClient`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ExternalClient [`claimbinding.ManagedConfigurator`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/claimbinding#ManagedConfigurator
[`resource.ManagedConfigurator`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ManagedConfigurator [`target.Reconciler`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/target#Reconciler
[`ResourceSpec`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceSpec [`ResourceSpec`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceSpec
[`ResourceStatus`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceStatus [`ResourceStatus`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceStatus
[`ResourceClaimSpec`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceClaimSpec [`ResourceClaimSpec`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceClaimSpec
[`ResourceClaimStatus`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceClaimStatus [`ResourceClaimStatus`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ResourceClaimStatus
[`ClassSpecTemplate`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ClassSpecTemplate [`ClassSpecTemplate`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ClassSpecTemplate
['resource.ExternalConnecter`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/resource#ExternalConnecter [`ProviderSpec`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/apis/core/v1alpha1#ProviderSpec
['managed.ExternalConnecter`]: https://godoc.org/github.com/crossplaneio/crossplane-runtime/pkg/reconciler/managed#ExternalConnecter
[opening a Crossplane issue]: https://github.com/crossplaneio/crossplane/issues/new/choose [opening a Crossplane issue]: https://github.com/crossplaneio/crossplane/issues/new/choose
[`GroupVersionKind`]: https://godoc.org/k8s.io/apimachinery/pkg/runtime/schema#GroupVersionKind [`GroupVersionKind`]: https://godoc.org/k8s.io/apimachinery/pkg/runtime/schema#GroupVersionKind
[`reconcile.Reconciler`]: https://godoc.org/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler [`reconcile.Reconciler`]: https://godoc.org/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler