Notes
  • Overview
  • Go
    • Basics
    • How To?
    • Tools & Libraries
    • Tutorials
    • Go Modules
    • Design Patterns
    • Guidelines
      • Code Review and Comments
    • Conversion
      • map[string]interface{} to JSON
      • map[string]interface{} to struct
    • Tips & Tricks
  • Docker
    • Blog Posts
    • Commands
  • Kubernetes
    • Courses
    • Concepts
    • Networking
      • Ingress Controller
    • KIND
    • Custom Resources
    • Tips & Tricks
      • Commands
        • Log
        • List
      • Container
        • Inject Executable Script
      • RBAC
        • Cross namespaced access
      • Patching Object
        • Restrict Key
      • Copy Object
  • Git
    • Rebase
  • System Design
    • Pub-Sub
      • Ovserver vs PubSub
  • Distributed System
    • Untitled
  • Linux
    • Bash
      • Basics
      • How To?
      • Pipe
      • Tips & Tricks
  • Security
    • Untitled
  • Database
    • PostgreSQL
      • Tricks & Tips
    • MySQL
      • Tips & Tricks
  • MISC
    • Development Patterns
    • Useful Github Apps/Actions
    • Parameters vs Arguments
    • Open API
  • Terminal
  • VS Code
Powered by GitBook
On this page

Was this helpful?

  1. Kubernetes
  2. Tips & Tricks
  3. Patching Object

Restrict Key

PreviousPatching ObjectNextCopy Object

Last updated 4 years ago

Was this helpful?

How to restrict patch from patching some specific key?

Problem:

We want to patch an object but we don't want to allow to modify some fields of the object.

Solution:

Use mergepatch.PreconditionFunc.

What is PreconditionFunc?

PreconditionFunc asserts that an incompatible change is not present within a patch. It takes generated patch and return if either patch is allowed(true) or forbidden(false) based on key's existent on patch.

type PreconditionFunc func(interface{}) bool

library has two helper function that return PreconditionFunc

// RequireKeyUnchanged returns a precondition function that fails if the provided key
// is present in the patch (indicating that its value has changed).
func RequireKeyUnchanged(key string) PreconditionFunc {
	return func(patch interface{}) bool {
		patchMap, ok := patch.(map[string]interface{})
		if !ok {
			return true
		}

		// The presence of key means that its value has been changed, so the test fails.
		_, ok = patchMap[key]
		return !ok
	}
}

and

// RequireMetadataKeyUnchanged creates a precondition function that fails
// if the metadata.key is present in the patch (indicating its value
// has changed).
func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
	return func(patch interface{}) bool {
		patchMap, ok := patch.(map[string]interface{})
		if !ok {
			return true
		}
		patchMap1, ok := patchMap["metadata"]
		if !ok {
			return true
		}
		patchMap2, ok := patchMap1.(map[string]interface{})
		if !ok {
			return true
		}
		_, ok = patchMap2[key]
		return !ok
	}
}
preconditions := []mergepatch.PreconditionFunc{
		mergepatch.RequireKeyUnchanged("apiVersion"),
		mergepatch.RequireMetadataKeyUnchanged("name"),
	}

patch, err := jsonpatch.CreateMergePatch(curJson, modJson)
if err != nil {
    return nil, err
}
if err := meetPreconditions(patch, preconditions...); err != nil {
    return nil, err
}
func meetPreconditions(patch []byte, fns ...mergepatch.PreconditionFunc) error {
    var patchMap map[string]interface{}
    if err := json.Unmarshal(patch, &patchMap); err != nil {
        return fmt.Errorf("failed to unmarshal patch for precondition check: %s", patch)
    }

    for _, fn := range fns {
        if !fn(patchMap) {
            return mergepatch.NewErrPreconditionFailed(patchMap)
        }
    }
    return nil
}

Using the above PreconditonFunc in

Creating Custom PreconditionFunc for

mergepatch
strategicpatch
jsonpatch