Skip to content

Commit

Permalink
add IgnoreHookError patch option (#538)
Browse files Browse the repository at this point in the history
Signed-off-by: Mikhail Scherba <[email protected]>
Co-authored-by: Mikhail Scherba <[email protected]>
Co-authored-by: Yuriy Losev <[email protected]>
  • Loading branch information
3 people authored Oct 13, 2023
1 parent f6471e2 commit aa38dfc
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 0 deletions.
25 changes: 25 additions & 0 deletions pkg/kube/object_patch/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type OperationSpec struct {
JSONPatch interface{} `json:"jsonPatch,omitempty" yaml:"jsonPatch,omitempty"`

IgnoreMissingObject bool `json:"ignoreMissingObject" yaml:"ignoreMissingObject"`
IgnoreHookError bool `json:"ignoreHookError" yaml:"ignoreHookError"`
}

type OperationType string
Expand All @@ -43,6 +44,25 @@ const (
JSONPatch OperationType = "JSONPatch"
)

// GetPatchStatusOperationsOnHookError returns list of Patch/Filter operations eligible for execution on Hook Error
func GetPatchStatusOperationsOnHookError(operations []Operation) []Operation {
patchStatusOperations := make([]Operation, 0)
for _, op := range operations {
switch operation := op.(type) {
case *filterOperation:
if operation.subresource == "/status" && operation.ignoreHookError {
patchStatusOperations = append(patchStatusOperations, operation)
}
case *patchOperation:
if operation.subresource == "/status" && operation.ignoreHookError {
patchStatusOperations = append(patchStatusOperations, operation)
}
}
}

return patchStatusOperations
}

func ParseOperations(specBytes []byte) ([]Operation, error) {
log.Debugf("parsing patcher operations:\n%s", specBytes)

Expand Down Expand Up @@ -120,6 +140,7 @@ type patchOperation struct {
patchType types.PatchType
patch interface{}
ignoreMissingObject bool
ignoreHookError bool
}

func (op *patchOperation) Description() string {
Expand All @@ -137,6 +158,7 @@ type filterOperation struct {
// Patch options.
filterFunc func(*unstructured.Unstructured) (*unstructured.Unstructured, error)
ignoreMissingObject bool
ignoreHookError bool
}

func (op *filterOperation) Description() string {
Expand Down Expand Up @@ -175,18 +197,21 @@ func NewFromOperationSpec(spec OperationSpec) Operation {
spec.ApiVersion, spec.Kind, spec.Namespace, spec.Name,
WithSubresource(spec.Subresource),
WithIgnoreMissingObject(spec.IgnoreMissingObject),
WithIgnoreHookError(spec.IgnoreHookError),
)
case MergePatch:
return NewMergePatchOperation(spec.MergePatch,
spec.ApiVersion, spec.Kind, spec.Namespace, spec.Name,
WithSubresource(spec.Subresource),
WithIgnoreMissingObject(spec.IgnoreMissingObject),
WithIgnoreHookError(spec.IgnoreHookError),
)
case JSONPatch:
return NewJSONPatchOperation(spec.JSONPatch,
spec.ApiVersion, spec.Kind, spec.Namespace, spec.Name,
WithSubresource(spec.Subresource),
WithIgnoreMissingObject(spec.IgnoreMissingObject),
WithIgnoreHookError(spec.IgnoreHookError),
)
}

Expand Down
21 changes: 21 additions & 0 deletions pkg/kube/object_patch/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,31 @@ func (s *subresourceHolder) applyToFilter(operation *filterOperation) {
operation.subresource = s.subresource
}

type ignoreHookError struct {
ignoreError bool
}

// IgnoreHookError allows applying patches for a Status subresource even if the hook fails
func IgnoreHookError() *ignoreHookError {
return WithIgnoreHookError(true)
}

func WithIgnoreHookError(ignoreError bool) *ignoreHookError {
return &ignoreHookError{ignoreError: ignoreError}
}

type ignoreMissingObject struct {
ignore bool
}

func (i *ignoreHookError) applyToPatch(operation *patchOperation) {
operation.ignoreHookError = i.ignoreError
}

func (i *ignoreHookError) applyToFilter(operation *filterOperation) {
operation.ignoreHookError = i.ignoreError
}

// IgnoreMissingObject do not return error if object exists for Patch and Filter operations.
func IgnoreMissingObject() *ignoreMissingObject {
return WithIgnoreMissingObject(true)
Expand Down
6 changes: 6 additions & 0 deletions pkg/kube/object_patch/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ func (o *ObjectPatcher) executeCreateOperation(op *createOperation) error {
// Other options:
// - WithSubresource — a subresource argument for Patch or Update API call.
// - IgnoreMissingObject — do not return error if the specified object is missing.
// - IgnoreHookError — allows applying patches for a Status subresource even if the hook fails
func (o *ObjectPatcher) executePatchOperation(op *patchOperation) error {
if op.patchType == types.MergePatchType {
log.Debug("Started MergePatchObject")
Expand Down Expand Up @@ -190,6 +191,11 @@ func (o *ObjectPatcher) executePatchOperation(op *patchOperation) error {

// executeFilterOperation retrieves a specified object, modified it with
// filterFunc and calls update.

// Other options:
// - WithSubresource — a subresource argument for Patch or Update API call.
// - IgnoreMissingObject — do not return error if the specified object is missing.
// - IgnoreHookError — allows applying patches for a Status subresource even if the hook fails
func (o *ObjectPatcher) executeFilterOperation(op *filterOperation) error {
var err error

Expand Down
3 changes: 3 additions & 0 deletions pkg/kube/object_patch/patch_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func (dop *PatchCollector) Delete(apiVersion, kind, namespace, name string, opti
// Options:
// - WithSubresource — a subresource argument for Patch call.
// - IgnoreMissingObject — do not return error if the specified object is missing.
// - IgnoreHookError — allows applying patches for a Status subresource even if the hook fails
func (dop *PatchCollector) MergePatch(mergePatch interface{}, apiVersion, kind, namespace, name string, options ...PatchOption) {
dop.add(NewMergePatchOperation(mergePatch, apiVersion, kind, namespace, name, options...))
}
Expand All @@ -52,6 +53,7 @@ func (dop *PatchCollector) MergePatch(mergePatch interface{}, apiVersion, kind,
// Options:
// - WithSubresource — a subresource argument for Patch call.
// - IgnoreMissingObject — do not return error if the specified object is missing.
// - IgnoreHookError — allows applying patches for a Status subresource even if the hook fails
func (dop *PatchCollector) JSONPatch(jsonPatch interface{}, apiVersion, kind, namespace, name string, options ...PatchOption) {
dop.add(NewJSONPatchOperation(jsonPatch, apiVersion, kind, namespace, name, options...))
}
Expand All @@ -62,6 +64,7 @@ func (dop *PatchCollector) JSONPatch(jsonPatch interface{}, apiVersion, kind, na
// Options:
// - WithSubresource — a subresource argument for Patch call.
// - IgnoreMissingObject — do not return error if the specified object is missing.
// - IgnoreHookError — allows applying patches for a Status subresource even if the hook fails
//
// Note: do not modify and return argument in filterFunc,
// use FromUnstructured to instantiate a concrete type or modify after DeepCopy.
Expand Down
3 changes: 3 additions & 0 deletions pkg/kube/object_patch/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ definitions:
type: string
ignoreMissingObject:
type: boolean
ignoreHookError:
type: boolean
type: object
additionalProperties: false
Expand All @@ -71,6 +73,7 @@ properties:
jqFilter: {}
mergePatch: {}
ignoreMissingObject: {}
ignoreHookError: {}
oneOf:
- allOf:
Expand Down
11 changes: 11 additions & 0 deletions pkg/shell-operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,17 @@ func (op *ShellOperator) HandleRunHook(t task.Task, taskHook *hook.Hook, hookMet

result, err := taskHook.Run(hookMeta.BindingType, hookMeta.BindingContext, hookLogLabels)
if err != nil {
if result != nil && len(result.KubernetesPatchBytes) > 0 {
operations, patchStatusErr := object_patch.ParseOperations(result.KubernetesPatchBytes)
if patchStatusErr != nil {
return fmt.Errorf("%s: couldn't patch status: %s", err, patchStatusErr)
}

patchStatusErr = op.ObjectPatcher.ExecuteOperations(object_patch.GetPatchStatusOperationsOnHookError(operations))
if patchStatusErr != nil {
return fmt.Errorf("%s: couldn't patch status: %s", err, patchStatusErr)
}
}
return err
}

Expand Down

0 comments on commit aa38dfc

Please sign in to comment.