Move composition resource interfaces and unstructured

implementations to crossplane-runtime.

Signed-off-by: Muvaffak Onus <onus.muvaffak@gmail.com>
This commit is contained in:
Muvaffak Onus 2020-04-21 03:15:17 +03:00
parent fb7ee9788b
commit e4cdc1545f
No known key found for this signature in database
GPG Key ID: 86E282DC72236827
11 changed files with 27 additions and 368 deletions

2
go.mod
View File

@ -4,6 +4,8 @@ go 1.13
replace sigs.k8s.io/controller-runtime => github.com/negz/controller-runtime v0.5.1-0.20200326231846-15460aedd9b7
replace github.com/crossplane/crossplane-runtime => github.com/muvaf/crossplane-runtime v0.0.0-20200421001124-4a7ece2ff53b
require (
github.com/crossplane/crossplane-runtime v0.7.1-0.20200421211018-be37c50cc2ab
github.com/crossplane/crossplane-tools v0.0.0-20200219001116-bb8b2ce46330

5
go.sum
View File

@ -72,9 +72,6 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/crossplane/crossplane-runtime v0.7.1-0.20200417213225-dab52011b1a5 h1:/aPN2KCcWycpGD/f/Dl8RSFT1IoaseF8L3EDN858NPQ=
github.com/crossplane/crossplane-runtime v0.7.1-0.20200417213225-dab52011b1a5/go.mod h1:e12p4X6dbtqd9GOnph78Epd6aezyvmcMM1+6aQy3VzY=
github.com/crossplane/crossplane-runtime v0.7.1-0.20200421211018-be37c50cc2ab/go.mod h1:e12p4X6dbtqd9GOnph78Epd6aezyvmcMM1+6aQy3VzY=
github.com/crossplane/crossplane-tools v0.0.0-20200219001116-bb8b2ce46330 h1:zxdYGQnQbfyZSnRbKUJ16x5lRzkUTbH+uumVq03ijYo=
github.com/crossplane/crossplane-tools v0.0.0-20200219001116-bb8b2ce46330/go.mod h1:C735A9X0x0lR8iGVOOxb49Mt70Ua4EM2b7PGaRPBLd4=
github.com/crossplane/oam-kubernetes-runtime v0.0.0-20200422175842-afde24fdf35b h1:V6ynh8p2gbPtisz2FHQM8cMpo4xscljzR5cEax8dtdM=
@ -310,6 +307,8 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
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/muvaf/crossplane-runtime v0.0.0-20200421001124-4a7ece2ff53b h1:nEGxXms83ArRimEDoT9BEnkowQzxsICAZla6IR4JWpQ=
github.com/muvaf/crossplane-runtime v0.0.0-20200421001124-4a7ece2ff53b/go.mod h1:e12p4X6dbtqd9GOnph78Epd6aezyvmcMM1+6aQy3VzY=
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 h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=

View File

@ -1,94 +0,0 @@
/*
Copyright 2020 The Crossplane 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"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
)
// ComposableResourceOption modifies the composable resource.
type ComposableResourceOption func(resource *ComposableResource)
// FromReference sets the metadata of ComposableResource.
func FromReference(ref corev1.ObjectReference) ComposableResourceOption {
return func(cr *ComposableResource) {
cr.SetGroupVersionKind(ref.GroupVersionKind())
cr.SetName(ref.Name)
cr.SetNamespace(ref.Namespace)
cr.SetUID(ref.UID)
}
}
// NewComposableResource returns a new *ComposableResource.
func NewComposableResource(opts ...ComposableResourceOption) *ComposableResource {
cr := &ComposableResource{}
for _, f := range opts {
f(cr)
}
return cr
}
// ComposableResource is used to operate on the composable resources whose schema
// is not known beforehand.
type ComposableResource struct {
unstructured.Unstructured
}
// GetUnstructured returns the underlying *unstructured.Unstructured.
func (cr *ComposableResource) GetUnstructured() *unstructured.Unstructured {
return &cr.Unstructured
}
// GetCondition of this ComposableResource.
func (cr *ComposableResource) GetCondition(ct v1alpha1.ConditionType) v1alpha1.Condition {
conditioned := v1alpha1.ConditionedStatus{}
// The path is directly `status` because conditions are inline.
if err := getObject(cr, "status", &conditioned); err != nil {
return v1alpha1.Condition{}
}
return conditioned.GetCondition(ct)
}
// SetConditions of this ComposableResource.
func (cr *ComposableResource) SetConditions(c ...v1alpha1.Condition) {
conditioned := v1alpha1.ConditionedStatus{}
// The path is directly `status` because conditions are inline.
_ = getObject(cr, "status", &conditioned)
conditioned.SetConditions(c...)
for i, ref := range conditioned.Conditions {
_ = setObject(cr, fmt.Sprintf("status.conditions[%d]", i), ref)
}
}
// GetWriteConnectionSecretToReference of this ComposableResource.
func (cr *ComposableResource) GetWriteConnectionSecretToReference() *v1alpha1.SecretReference {
out := &v1alpha1.SecretReference{}
if err := getObject(cr, "spec.writeConnectionSecretToRef", out); err != nil {
return nil
}
return out
}
// SetWriteConnectionSecretToReference of this ComposableResource.
func (cr *ComposableResource) SetWriteConnectionSecretToReference(r *v1alpha1.SecretReference) {
_ = setObject(cr, "spec.writeConnectionSecretToRef", r)
}

View File

@ -1,213 +0,0 @@
/*
Copyright 2020 The Crossplane 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 (
"encoding/json"
"k8s.io/apimachinery/pkg/runtime/schema"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
)
// CompositeResourceOption is used to configure *CompositeResource
type CompositeResourceOption func(*CompositeResource)
// WithGroupVersionKind sets the GroupVersionKind.
func WithGroupVersionKind(gvk schema.GroupVersionKind) CompositeResourceOption {
return func(c *CompositeResource) {
c.SetGroupVersionKind(gvk)
}
}
// NewCompositeResource returns a new *CompositeResource configured via opts.
func NewCompositeResource(opts ...CompositeResourceOption) *CompositeResource {
c := &CompositeResource{}
for _, f := range opts {
f(c)
}
return c
}
// An CompositeResource is the internal representation of the resource generated
// via Crossplane definition types. It is only used for operations in the controller,
// it's not intended to be stored in the api-server.
type CompositeResource struct {
unstructured.Unstructured
}
// GetUnstructured returns the underlying *unstructured.Unstructured.
func (c *CompositeResource) GetUnstructured() *unstructured.Unstructured {
return &c.Unstructured
}
// GetCompositionSelector returns the composition selector.
func (c *CompositeResource) GetCompositionSelector() *v1.LabelSelector {
out := &v1.LabelSelector{}
if err := getObject(c, "spec.compositionSelector", out); err != nil {
return nil
}
return out
}
// SetCompositionSelector sets the composition selector.
func (c *CompositeResource) SetCompositionSelector(sel *v1.LabelSelector) {
_ = setObject(c, "spec.compositionSelector", sel)
}
// GetCompositionReference returns the composition reference.
func (c *CompositeResource) GetCompositionReference() *corev1.ObjectReference {
out := &corev1.ObjectReference{}
if err := getObject(c, "spec.compositionRef", out); err != nil {
return nil
}
return out
}
// SetCompositionReference sets the composition reference.
func (c *CompositeResource) SetCompositionReference(ref *corev1.ObjectReference) {
_ = setObject(c, "spec.compositionRef", ref)
}
// GetResourceReferences returns the references of composed resources.
func (c *CompositeResource) GetResourceReferences() []corev1.ObjectReference {
out := &[]corev1.ObjectReference{}
_ = getObject(c, "spec.resourceRefs", out)
return *out
}
// SetResourceReferences sets the references of composed resources.
func (c *CompositeResource) SetResourceReferences(refs []corev1.ObjectReference) {
empty := corev1.ObjectReference{}
finalRefs := []corev1.ObjectReference{}
for _, ref := range refs {
// TODO(muvaf): temporary workaround.
if ref.String() == empty.String() {
continue
}
finalRefs = append(finalRefs, ref)
}
_ = setArray(c, "spec.resourceRefs", finalRefs)
}
// GetWriteConnectionSecretToReference returns the connection secret reference.
func (c *CompositeResource) GetWriteConnectionSecretToReference() *v1alpha1.SecretReference {
out := &v1alpha1.SecretReference{}
if err := getObject(c, "spec.writeConnectionSecretToRef", out); err != nil {
return nil
}
return out
}
// SetWriteConnectionSecretToReference sets the connection secret reference.
func (c *CompositeResource) SetWriteConnectionSecretToReference(ref *v1alpha1.SecretReference) {
_ = setObject(c, "spec.writeConnectionSecretToRef", ref)
}
// GetCondition of this CompositeResource.
func (c *CompositeResource) GetCondition(ct v1alpha1.ConditionType) v1alpha1.Condition {
conditioned := v1alpha1.ConditionedStatus{}
// The path is directly `status` because conditions are inline.
if err := getObject(c, "status", &conditioned); err != nil {
return v1alpha1.Condition{}
}
return conditioned.GetCondition(ct)
}
// SetConditions of this CompositeResource.
func (c *CompositeResource) SetConditions(conditions ...v1alpha1.Condition) {
conditioned := v1alpha1.ConditionedStatus{}
// The path is directly `status` because conditions are inline.
_ = getObject(c, "status", &conditioned)
conditioned.SetConditions(conditions...)
_ = setArray(c, "status.conditions", conditioned.Conditions)
}
// SetBindingPhase of this CompositeResource.
func (c *CompositeResource) SetBindingPhase(p v1alpha1.BindingPhase) {
_ = setObject(c, "status.bindingPhase", p)
}
// GetBindingPhase of this CompositeResource.
func (c *CompositeResource) GetBindingPhase() v1alpha1.BindingPhase {
bp := ""
_ = getObject(c, "status.bindingPhase", &bp)
return v1alpha1.BindingPhase(bp)
}
// CompositeResourceList contains a list of CompositeResources.
type CompositeResourceList struct {
unstructured.UnstructuredList
}
// GetUnstructuredList returns the underlying *unstructured.UnstructuredList.
func (c *CompositeResourceList) GetUnstructuredList() *unstructured.UnstructuredList {
return &c.UnstructuredList
}
func getObject(in interface{ UnstructuredContent() map[string]interface{} }, path string, target interface{}) error {
p := fieldpath.Pave(in.UnstructuredContent())
obj, err := p.GetValue(path)
if err != nil {
return err
}
if obj == nil {
return nil
}
js, err := json.Marshal(obj)
if err != nil {
return err
}
return json.Unmarshal(js, target)
}
func setObject(in interface{ UnstructuredContent() map[string]interface{} }, path string, input interface{}) error {
p := fieldpath.Pave(in.UnstructuredContent())
if input == nil {
return p.SetValue(path, nil)
}
js, err := json.Marshal(input)
if err != nil {
return err
}
out := map[string]interface{}{}
if err := json.Unmarshal(js, &out); err != nil {
return err
}
return p.SetValue(path, out)
}
func setArray(in interface{ UnstructuredContent() map[string]interface{} }, path string, input interface{}) error {
p := fieldpath.Pave(in.UnstructuredContent())
if input == nil {
return p.SetValue(path, nil)
}
js, err := json.Marshal(input)
if err != nil {
return err
}
out := []interface{}{}
if err := json.Unmarshal(js, &out); err != nil {
return err
}
return p.SetValue(path, out)
}

View File

@ -1,36 +0,0 @@
package composed
import (
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/crossplane/crossplane-runtime/pkg/resource"
)
// Composite resource managed one or more Composable resources.
type Composite interface {
v1.Object
runtime.Unstructured
resource.Conditioned
resource.Bindable
resource.ConnectionSecretWriterTo
SetCompositionSelector(*v1.LabelSelector)
GetCompositionSelector() *v1.LabelSelector
SetCompositionReference(*corev1.ObjectReference)
GetCompositionReference() *corev1.ObjectReference
SetResourceReferences([]corev1.ObjectReference)
GetResourceReferences() []corev1.ObjectReference
}
// Composable resources can be a resource in a composition.
type Composable interface {
resource.Object
resource.Conditioned
resource.ConnectionSecretWriterTo
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package composed
package composite
import (
"context"
@ -90,7 +90,7 @@ type SelectorResolver struct {
}
// ResolveSelector resolves selector to a reference if it doesn't exist.
func (r *SelectorResolver) ResolveSelector(ctx context.Context, cr Composite) error {
func (r *SelectorResolver) ResolveSelector(ctx context.Context, cr resource.Composite) error {
if cr.GetCompositionReference() != nil {
return nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package composed
package composite
import (
"context"

View File

@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package composed
package composite
import (
"context"
"fmt"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@ -32,7 +34,6 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane/apis/apiextensions/v1alpha1"
"github.com/crossplane/crossplane/pkg/controller/apiextensions/composed/api"
)
// Observation is the result of composed reconciliation.
@ -58,28 +59,28 @@ type APIComposedReconciler struct {
// Reconcile tries to bring the composed resource into the desired state. It
// creates the resource if the given reference is empty.
func (r *APIComposedReconciler) Reconcile(ctx context.Context, cr Composite, composedRef v1.ObjectReference, tmpl v1alpha1.ComposedTemplate) (Observation, error) {
func (r *APIComposedReconciler) Reconcile(ctx context.Context, cr resource.Composite, composedRef v1.ObjectReference, tmpl v1alpha1.ComposedTemplate) (Observation, error) {
// Deletion of the composite resource has been triggered. We make deletion
// the deletion call and report back success only if the call returns NotFound.
if meta.WasDeleted(cr) {
if composedRef.Name == "" {
return Observation{}, nil
}
err := r.client.Delete(ctx, api.NewComposableResource(api.FromReference(composedRef)))
err := r.client.Delete(ctx, unstructured.NewComposed(unstructured.FromReference(composedRef)))
if resource.IgnoreNotFound(err) != nil {
return Observation{}, err
}
return Observation{}, nil
}
var composed Composable
var composed resource.Composable
if composedRef.Name == "" {
composed = api.NewComposableResource()
composed = unstructured.NewComposed()
if err := r.Configure(cr, composed, tmpl); err != nil {
return Observation{}, err
}
} else {
composed = api.NewComposableResource(api.FromReference(composedRef))
composed = unstructured.NewComposed(unstructured.FromReference(composedRef))
if err := r.client.Get(ctx, types.NamespacedName{Name: composed.GetName(), Namespace: composed.GetNamespace()}, composed); err != nil {
return Observation{}, err
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package composed
package composite
import (
"github.com/pkg/errors"

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package composed
package composite
import (
"context"
@ -33,9 +33,9 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/resource/unstructured"
"github.com/crossplane/crossplane/apis/apiextensions/v1alpha1"
"github.com/crossplane/crossplane/pkg/controller/apiextensions/composed/api"
)
const (
@ -66,7 +66,7 @@ type ConnectionPublisher interface {
// NewCompositeReconciler returns a new *compositeReconciler.
func NewCompositeReconciler(name string, mgr manager.Manager, gvk schema.GroupVersionKind, log logging.Logger, filterer ConnectionSecretFilterer) reconcile.Reconciler {
nc := func() Composite { return api.NewCompositeResource(api.WithGroupVersionKind(gvk)) }
nc := func() resource.Composite { return unstructured.NewComposite(unstructured.WithGroupVersionKind(gvk)) }
kube := NewClientForUnregistered(mgr.GetClient())
return &compositeReconciler{
@ -83,18 +83,18 @@ func NewCompositeReconciler(name string, mgr manager.Manager, gvk schema.GroupVe
// ComposableReconciler is able to reconcile a member of the composite resource.
type ComposableReconciler interface {
Reconcile(ctx context.Context, cr Composite, composedRef v1.ObjectReference, tmpl v1alpha1.ComposedTemplate) (Observation, error)
Reconcile(ctx context.Context, cr resource.Composite, composedRef v1.ObjectReference, tmpl v1alpha1.ComposedTemplate) (Observation, error)
}
// Resolver selects the composition reference with the information given as selector.
type Resolver interface {
ResolveSelector(ctx context.Context, cr Composite) error
ResolveSelector(ctx context.Context, cr resource.Composite) error
}
// compositeReconciler reconciles the generic CRD that is generated via InfrastructureDefinition.
type compositeReconciler struct {
client client.Client
newComposite func() Composite
newComposite func() resource.Composite
composed ComposableReconciler
connection ConnectionPublisher
finalizer resource.Finalizer

View File

@ -37,7 +37,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane/apis/apiextensions/v1alpha1"
"github.com/crossplane/crossplane/pkg/controller/apiextensions/composed"
"github.com/crossplane/crossplane/pkg/controller/apiextensions/composite"
)
const (
@ -82,7 +82,7 @@ func Setup(mgr ctrl.Manager, log logging.Logger) error {
// NewReconciler returns a new *reconciler.
func NewReconciler(mgr manager.Manager, opts ...ReconcilerOption) reconcile.Reconciler {
kube := composed.NewClientForUnregistered(mgr.GetClient())
kube := composite.NewClientForUnregistered(mgr.GetClient())
newDefinerFn := func() Definer { return &v1alpha1.InfrastructureDefinition{} }
r := &reconciler{
client: kube,
@ -99,7 +99,7 @@ func NewReconciler(mgr manager.Manager, opts ...ReconcilerOption) reconcile.Reco
}
// TODO(muvaf): we don't have a use case but it'd be consistent if we allowed
// different controller engines to be configured via ReconcilerOption.
r.ctrl = composed.NewControllerEngine(mgr, r.log)
r.ctrl = composite.NewControllerEngine(mgr, r.log)
return r
}
@ -128,7 +128,7 @@ func WithRecorder(recorder event.Recorder) ReconcilerOption {
type reconciler struct {
client client.Client
mgr manager.Manager
ctrl *composed.ControllerEngine
ctrl *composite.ControllerEngine
resource.Finalizer
crd Client
newDefiner func() Definer
@ -193,7 +193,7 @@ func (r *reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error)
// We know that CRD is ready and we are in control of it. So, we'll spin up
// an instance controller to reconcile it.
reconciler := composed.NewCompositeReconciler(definer.GetCRDName(), r.mgr, definer.GetCRDGroupVersionKind(), r.log, definer)
reconciler := composite.NewCompositeReconciler(definer.GetCRDName(), r.mgr, definer.GetCRDGroupVersionKind(), r.log, definer)
if err := r.ctrl.Start(definer.GetCRDName(), definer.GetCRDGroupVersionKind(), reconciler); err != nil {
log.Debug(errCannotStartController, "error", err)
definer.Status.SetConditions(runtimev1alpha1.ReconcileError(errors.Wrap(err, errCannotStartController)))