Skip to content

Commit

Permalink
Merge pull request #21 from morningspace/feature-mgmtpolicy-reference
Browse files Browse the repository at this point in the history
Implement feature management policy and reference
  • Loading branch information
turkenh committed Feb 18, 2022
2 parents 4d722ef + 46ef0d7 commit 9bfb526
Show file tree
Hide file tree
Showing 15 changed files with 1,317 additions and 72 deletions.
128 changes: 127 additions & 1 deletion apis/object/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,85 @@ package v1alpha1

import (
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

// ObjectAction defines actions applicable to Object
type ObjectAction string

// A ManagementPolicy determines what should happen to the underlying external
// resource when a managed resource is created, updated, deleted, or observed.
// +kubebuilder:validation:Enum=Default;ObserveCreateUpdate;ObserveDelete;Observe
type ManagementPolicy string

const (
// Default means the provider can fully manage the resource.
Default ManagementPolicy = "Default"
// ObserveCreateUpdate means the provider can observe, create, or update
// the resource, but can not delete it.
ObserveCreateUpdate ManagementPolicy = "ObserveCreateUpdate"
// ObserveDelete means the provider can observe or delete the resource, but
// can not create and update it.
ObserveDelete ManagementPolicy = "ObserveDelete"
// Observe means the provider can only observe the resource.
Observe ManagementPolicy = "Observe"

// ObjectActionCreate means to create an Object
ObjectActionCreate ObjectAction = "Create"
// ObjectActionUpdate means to update an Object
ObjectActionUpdate ObjectAction = "Update"
// ObjectActionDelete means to delete an Object
ObjectActionDelete ObjectAction = "Delete"
)

// DependsOn refers to an object by Name, Kind, APIVersion, etc. It is used to
// reference other Object or arbitrary Kubernetes resource which is either
// cluster or namespace scoped.
type DependsOn struct {
// APIVersion of the referenced object.
// +kubebuilder:default=kubernetes.crossplane.io/v1alpha1
// +optional
APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referenced object.
// +kubebuilder:default=Object
// +optional
Kind string `json:"kind,omitempty"`
// Name of the referenced object.
Name string `json:"name"`
// Namespace of the referenced object.
// +optional
Namespace string `json:"namespace,omitempty"`
}

// PatchesFrom refers to an object by Name, Kind, APIVersion, etc., and patch
// fields from this object.
type PatchesFrom struct {
DependsOn `json:",inline"`
// FieldPath is the path of the field on the resource whose value is to be
// used as input.
FieldPath *string `json:"fieldPath"`
}

// Reference refers to an Object or arbitrary Kubernetes resource and optionally
// patch values from that resource to the current Object.
type Reference struct {
// DependsOn is used to declare dependency on other Object or arbitrary
// Kubernetes resource.
// +optional
*DependsOn `json:"dependsOn,omitempty"`
// PatchesFrom is used to declare dependency on other Object or arbitrary
// Kubernetes resource, and also patch fields from this object.
// +optional
*PatchesFrom `json:"patchesFrom,omitempty"`
// ToFieldPath is the path of the field on the resource whose value will
// be changed with the result of transforms. Leave empty if you'd like to
// propagate to the same path as patchesFrom.fieldPath.
// +optional
ToFieldPath *string `json:"toFieldPath,omitempty"`
}

// ObjectParameters are the configurable fields of a Object.
type ObjectParameters struct {
// Raw JSON representation of the kubernetes object to be created.
Expand All @@ -41,7 +116,10 @@ type ObjectObservation struct {
// A ObjectSpec defines the desired state of a Object.
type ObjectSpec struct {
xpv1.ResourceSpec `json:",inline"`
ForProvider ObjectParameters `json:"forProvider"`
// +kubebuilder:default=Default
ManagementPolicy `json:"managementPolicy,omitempty"`
References []Reference `json:"references,omitempty"`
ForProvider ObjectParameters `json:"forProvider"`
}

// A ObjectStatus represents the observed state of a Object.
Expand Down Expand Up @@ -74,3 +152,51 @@ type ObjectList struct {
metav1.ListMeta `json:"metadata,omitempty"`
Items []Object `json:"items"`
}

// ApplyFromFieldPathPatch patches the "to" resource, using a source field
// on the "from" resource.
func (r *Reference) ApplyFromFieldPathPatch(from, to runtime.Object) error {
// Default to patch the same field on the "to" resource.
if r.ToFieldPath == nil {
r.ToFieldPath = r.PatchesFrom.FieldPath
}

paved, err := fieldpath.PaveObject(from)
if err != nil {
return err
}

out, err := paved.GetValue(*r.PatchesFrom.FieldPath)
if err != nil {
return err
}

return patchFieldValueToObject(*r.ToFieldPath, out, to)
}

// patchFieldValueToObject, given a path, value and "to" object, will
// apply the value to the "to" object at the given path, returning
// any errors as they occur.
func patchFieldValueToObject(path string, value interface{}, to runtime.Object) error {
paved, err := fieldpath.PaveObject(to)
if err != nil {
return err
}

err = paved.SetValue("spec.forProvider.manifest."+path, value)
if err != nil {
return err
}

return runtime.DefaultUnstructuredConverter.FromUnstructured(paved.UnstructuredContent(), to)
}

// IsActionAllowed determines if action is allowed to be performed on Object
func (p *ManagementPolicy) IsActionAllowed(action ObjectAction) bool {
if action == ObjectActionCreate || action == ObjectActionUpdate {
return *p == Default || *p == ObserveCreateUpdate
}

// ObjectActionDelete
return *p == Default || *p == ObserveDelete
}
73 changes: 73 additions & 0 deletions apis/object/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions examples/object/policy/default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
name: foo
spec:
# Use management policy Default to fully control k8s resource
# It is the default policy that can be omitted
forProvider:
manifest:
apiVersion: v1
kind: ConfigMap
metadata:
# name in manifest is optional and defaults to Object name
# name: some-other-name
namespace: default
providerConfigRef:
name: kubernetes-provider
21 changes: 21 additions & 0 deletions examples/object/policy/observe-create-update.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
name: foo
spec:
# Use management policy ObserveCreateUpdate to observe, create, or update k8s
# resource, but leave to third party to delete the resource
managementPolicy: ObserveCreateUpdate
forProvider:
manifest:
apiVersion: v1
kind: ConfigMap
metadata:
# name in manifest is optional and defaults to Object name
# name: some-other-name
namespace: default
data:
sample-key: sample-value
providerConfigRef:
name: kubernetes-provider
26 changes: 26 additions & 0 deletions examples/object/policy/observe-delete.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: foo
data:
sample-key: sample-value
---
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
name: foo
spec:
# Use management policy ObserveDelete to observe or delete k8s resource,
# but leave to third party to create or update the resource
managementPolicy: ObserveDelete
forProvider:
manifest:
apiVersion: v1
kind: ConfigMap
metadata:
# name in manifest is optional and defaults to Object name
# name: some-other-name
namespace: default
providerConfigRef:
name: kubernetes-provider
26 changes: 26 additions & 0 deletions examples/object/policy/observe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: foo
data:
sample-key: sample-value
---
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
name: foo
spec:
# Use management policy Observe to observe k8s resource,
# but leave to third party to create, update, or delete the resource
managementPolicy: Observe
forProvider:
manifest:
apiVersion: v1
kind: ConfigMap
metadata:
# name in manifest is optional and defaults to Object name
# name: some-other-name
namespace: default
providerConfigRef:
name: kubernetes-provider
39 changes: 39 additions & 0 deletions examples/object/references/depends-on-object.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
name: foo
spec:
references:
# Use dependsOn to declare dependency on other object for this object
- dependsOn:
# apiVersion is optional and defaults to Object apiVersion
# kind is optional and defaults to Object kind
# namespace is not needed when it is cluster-scoped resource
name: bar
forProvider:
manifest:
apiVersion: v1
kind: ConfigMap
metadata:
namespace: default
data:
sample-key: sample-value
providerConfigRef:
name: kubernetes-provider
---
apiVersion: kubernetes.crossplane.io/v1alpha1
kind: Object
metadata:
name: bar
spec:
forProvider:
manifest:
apiVersion: v1
kind: ConfigMap
metadata:
namespace: default
data:
sample-key: sample-value
providerConfigRef:
name: kubernetes-provider
Loading

0 comments on commit 9bfb526

Please sign in to comment.