Skip to content

Commit

Permalink
New patch (#80)
Browse files Browse the repository at this point in the history
* some initial design

Signed-off-by: raffaelespazzoli <raffaele.spazzoli@gmail.com>

* new patch

Signed-off-by: raffaelespazzoli <raffaele.spazzoli@gmail.com>

* fixed manifest generation

Signed-off-by: raffaelespazzoli <raffaele.spazzoli@gmail.com>

* Update README.md

Co-authored-by: Andrew Block <andy.block@gmail.com>

* Update README.md

Co-authored-by: Andrew Block <andy.block@gmail.com>

* Update README.md

Co-authored-by: Andrew Block <andy.block@gmail.com>

* Update README.md

Co-authored-by: Andrew Block <andy.block@gmail.com>

* fixes from andy's comments

Signed-off-by: raffaelespazzoli <raffaele.spazzoli@gmail.com>

Co-authored-by: Andrew Block <andy.block@gmail.com>
  • Loading branch information
raffaelespazzoli and sabre1041 authored Dec 10, 2021
1 parent db103cf commit b22e79c
Show file tree
Hide file tree
Showing 36 changed files with 2,094 additions and 2,031 deletions.
72 changes: 62 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,23 @@ This library layers on top of the Operator SDK and with the objective of helping

This library covers three main areas:

1. [Idempotent methods](#Idempotent-Methods-to-Manipulate-Resources) to manipulate resources and arrays of resources
2. [Basic operator lifecycle](#Basic-Operator-Lifecycle-Management) needs (validation, initialization, status and error management, finalization)
3. [Enforcing resources operator support](#Enforcing-Resource-Operator-Support). For those operators which calculate a set of resources that need to exist and then enforce them, generalized support for the enforcing phase is provided.
1. [Utility Methods](#Utility-Methods) Utility methods that are callable by any operator.
2. [Idempotent methods](#Idempotent-Methods-to-Manipulate-Resources) to manipulate resources and arrays of resources
3. [Basic operator lifecycle](#Basic-Operator-Lifecycle-Management) needs (validation, initialization, status and error management, finalization)
4. [Enforcing resources operator support](#Enforcing-Resource-Operator-Support). For those operators which calculate a set of resources that need to exist and then enforce them, generalized support for the enforcing phase is provided.

## Utility Methods

Prior to version v2.x the general philosophy of this library was that new operator would inherit from `ReconcilerBase` and in doing so they would have access to a bunch of utility methods.
With release v2.0.0 a new approach is available. Utility methods are callable by any operator having to inherit. This makes it easier to use this library and does not conflict with autogenerate code from `kube-builder` and `operator-sdk`.
Most of the Utility methods receive a context.Context parameter. Normally this context must be initialized with a `logr.Logger` and a `rest.Config`. Some utility methods may require more, see each individual documentation.

Utility methods are currently organized in the following folders:

1. crud: idempotent create/update/delete functions.
2. discoveryclient: methods related to the discovery client, typically used to load `apiResource` objects.
3. dynamicclient: methods related to building client based on object whose type is not known at compile time.
4. templates: utility methods for dealing with templates whose output is an object or a list of objects.

## Idempotent Methods to Manipulate Resources

Expand All @@ -29,6 +43,15 @@ Also there are utility methods to manage finalizers, test ownership and process

## Basic Operator Lifecycle Management

---

Note

This part of the library is largely deprecated. For initialization and defaulting a MutatingWebHook should be used. For validation a Validating WebHook should be used.
The part regarding the finalization is still relevant.

---

To get started with this library do the following:

Change your reconciler initialization as exemplified below to add a set of utility methods to it
Expand Down Expand Up @@ -172,9 +195,11 @@ return r.ManageSuccessWithRequeue(ctx, instance, 3*time.Second)
```

or simply using the convenience function:

```go
return r.ManageOutcomeWithRequeue(ctx, instance, err, 3*time.Second)
```

which will delegate to the error or success variant depending on `err` being `nil` or not.

### Managing CR Finalization
Expand Down Expand Up @@ -287,17 +312,33 @@ In some situations, a patch must be parametric on some state of the cluster. For
A patch is defined as follows:

```golang
type LockedPatch struct {
ID string `json:"id,omitempty"`
SourceObjectRefs []corev1.ObjectReference `json:"sourceObjectRefs,omitempty"`
TargetObjectRef corev1.ObjectReference `json:"targetObjectRef,omitempty"`
PatchType types.PatchType `json:"patchType,omitempty"`
PatchTemplate string `json:"patchTemplate,omitempty"`
Template template.Template `json:"-"`
type LockedPatch struct {
Name string `json:"name,omitempty"`
SourceObjectRefs []utilsapi.SourceObjectReference `json:"sourceObjectRefs,omitempty"`
TargetObjectRef utilsapi.TargetObjectReference `json:"targetObjectRef,omitempty"`
PatchType types.PatchType `json:"patchType,omitempty"`
PatchTemplate string `json:"patchTemplate,omitempty"`
Template template.Template `json:"-"`
}
```

the targetObjectRef and sourceObjectRefs are watched for changes by the reconciler.

targetObjectRef can select multiple objects, this is the logic

| Namespaced Type | Namespace | Name | Selection type |
| --- | --- | --- | --- |
| yes | null | null | multiple selection across namespaces |
| yes | null | not null | multiple selection across namespaces where the name corresponds to the passed name |
| yes | not null | null | multiple selection within a namespace |
| yes | not null | not nul | single selection |
| no | N/A | null | multiple selection |
| no | N/A | not null | single selection |

Selection can be further narrowed down by filtering by labels and/or annotations. The patch will be applied to all of the selected instances.

Name and Namespace of sourceRefObjects are interpreted as golang templates with the current target instance and the only parameter. This allows to select different source object for each target object.

The relevant part of the operator code would look like this:

```golang
Expand Down Expand Up @@ -411,6 +452,17 @@ oc login --token ${token}
make run ENABLE_WEBHOOKS=false
```

### testing

Patches

```shell
oc new-project patch-test
oc create sa test -n patch-test
oc adm policy add-cluster-role-to-user cluster-reader -z default -n patch-test
oc apply -f ./test/enforcing-patch.yaml -n patch-test
```

## Building/Pushing the operator image

```shell
Expand Down
9 changes: 4 additions & 5 deletions api/v1alpha1/enforcingcrd_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package v1alpha1

import (
"github.com/redhat-cop/operator-utils/pkg/util/apis"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -32,7 +31,7 @@ type EnforcingCRDSpec struct {
// Resources is a list of resource manifests that should be locked into the specified configuration
// +kubebuilder:validation:Optional
// +listType=atomic
Resources []apis.LockedResource `json:"resources,omitempty"`
Resources []LockedResource `json:"resources,omitempty"`
}

// EnforcingCRDStatus defines the observed state of EnforcingCRD
Expand All @@ -41,14 +40,14 @@ type EnforcingCRDStatus struct {
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
// +kubebuilder:validation:Optional
apis.EnforcingReconcileStatus `json:",inline,omitempty"`
EnforcingReconcileStatus `json:",inline,omitempty"`
}

func (m *EnforcingCRD) GetEnforcingReconcileStatus() apis.EnforcingReconcileStatus {
func (m *EnforcingCRD) GetEnforcingReconcileStatus() EnforcingReconcileStatus {
return m.Status.EnforcingReconcileStatus
}

func (m *EnforcingCRD) SetEnforcingReconcileStatus(reconcileStatus apis.EnforcingReconcileStatus) {
func (m *EnforcingCRD) SetEnforcingReconcileStatus(reconcileStatus EnforcingReconcileStatus) {
m.Status.EnforcingReconcileStatus = reconcileStatus
}

Expand Down
10 changes: 4 additions & 6 deletions api/v1alpha1/enforcingpatch_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package v1alpha1

import (
"github.com/redhat-cop/operator-utils/pkg/util/apis"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -32,23 +31,22 @@ type EnforcingPatchSpec struct {

// Patches is a list of pacthes that should be encforced at runtime.
// +kubebuilder:validation:Optional
// +listType=atomic
Patches []apis.Patch `json:"patches,omitempty"`
Patches map[string]Patch `json:"patches,omitempty"`
}

// EnforcingPatchStatus defines the observed state of EnforcingPatch
type EnforcingPatchStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
apis.EnforcingReconcileStatus `json:",inline,omitempty"`
EnforcingReconcileStatus `json:",inline,omitempty"`
}

func (m *EnforcingPatch) GetEnforcingReconcileStatus() apis.EnforcingReconcileStatus {
func (m *EnforcingPatch) GetEnforcingReconcileStatus() EnforcingReconcileStatus {
return m.Status.EnforcingReconcileStatus
}

func (m *EnforcingPatch) SetEnforcingReconcileStatus(reconcileStatus apis.EnforcingReconcileStatus) {
func (m *EnforcingPatch) SetEnforcingReconcileStatus(reconcileStatus EnforcingReconcileStatus) {
m.Status.EnforcingReconcileStatus = reconcileStatus
}

Expand Down
39 changes: 39 additions & 0 deletions api/v1alpha1/enforcingreconcilerstatus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
type Conditions []metav1.Condition

// +mapType=granular
type ConditionMap map[string]Conditions

// EnforcingReconcileStatus represents the status of the last reconcile cycle. It's used to communicate success or failure and the error message
type EnforcingReconcileStatus struct {

// ReconcileStatus this is the general status of the main reconciler
// +kubebuilder:validation:Optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

//LockedResourceStatuses contains the reconcile status for each of the managed resources
// +kubebuilder:validation:Optional
LockedResourceStatuses map[string]Conditions `json:"lockedResourceStatuses,omitempty"`

//LockedResourceStatuses contains the reconcile status for each of the managed resources
// +kubebuilder:validation:Optional
LockedPatchStatuses map[string]ConditionMap `json:"lockedPatchStatuses,omitempty"`
}

// EnforcingReconcileStatusAware is an interfce that must be implemented by a CRD type that has been enabled with ReconcileStatus, it can then benefit of a series of utility methods.
// +kubebuilder:object:generate:=false
type EnforcingReconcileStatusAware interface {
GetEnforcingReconcileStatus() EnforcingReconcileStatus
SetEnforcingReconcileStatus(enforcingReconcileStatus EnforcingReconcileStatus)
}
Loading

0 comments on commit b22e79c

Please sign in to comment.