Skip to content

Commit

Permalink
util: Move patch.go into its own package
Browse files Browse the repository at this point in the history
Also, rename some bits so that it more closely aligns with
github.com/evanphx/json-patch, so it's simpler for us to use in the
control plane.
  • Loading branch information
sharnoff committed Sep 29, 2023
1 parent af638e2 commit be3b8d1
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 42 deletions.
16 changes: 8 additions & 8 deletions neonvm/controllers/virtualmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import (
"github.com/neondatabase/autoscaling/neonvm/pkg/ipam"

"github.com/neondatabase/autoscaling/pkg/api"
"github.com/neondatabase/autoscaling/pkg/util"
patchutil "github.com/neondatabase/autoscaling/pkg/util/patch"
)

const (
Expand Down Expand Up @@ -723,7 +723,7 @@ func (r *VirtualMachineReconciler) doReconcile(ctx context.Context, virtualmachi
func updatePodMetadataIfNecessary(ctx context.Context, c client.Client, vm *vmv1.VirtualMachine, runnerPod *corev1.Pod) error {
log := log.FromContext(ctx)

var patches []util.JSONPatch
var patches []patchutil.Operation

metaSpecs := []struct {
metaField string
Expand Down Expand Up @@ -756,7 +756,7 @@ func updatePodMetadataIfNecessary(ctx context.Context, c client.Client, vm *vmv1
// Add/update the entries we're expecting to be there
for k, e := range spec.expected {
if a, ok := spec.actual[k]; !ok || e != a {
patches = append(patches, util.JSONPatch{
patches = append(patches, patchutil.Operation{
// From RFC 6902 (JSON patch):
//
// > The "add" operation performs one of the following functions, depending upon
Expand All @@ -770,8 +770,8 @@ func updatePodMetadataIfNecessary(ctx context.Context, c client.Client, vm *vmv1
// > member's value is replaced.
//
// So: if the value is missing we'll add it. And if it's different, we'll replace it.
Op: util.PatchAdd,
Path: fmt.Sprintf("/metadata/%s/%s", spec.metaField, util.PatchPathEscape(k)),
Op: patchutil.OpAdd,
Path: fmt.Sprintf("/metadata/%s/%s", spec.metaField, patchutil.PathEscape(k)),
Value: e,
})
}
Expand All @@ -782,9 +782,9 @@ func updatePodMetadataIfNecessary(ctx context.Context, c client.Client, vm *vmv1
for k := range spec.actual {
if _, expected := spec.expected[k]; !expected && !spec.ignoreExtra[k] {
removed = append(removed, k)
patches = append(patches, util.JSONPatch{
Op: util.PatchRemove,
Path: fmt.Sprintf("/metadata/%s/%s", spec.metaField, util.PatchPathEscape(k)),
patches = append(patches, patchutil.Operation{
Op: patchutil.OpRemove,
Path: fmt.Sprintf("/metadata/%s/%s", spec.metaField, patchutil.PathEscape(k)),
})
}
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/agent/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import (
"github.com/neondatabase/autoscaling/pkg/agent/schedwatch"
"github.com/neondatabase/autoscaling/pkg/api"
"github.com/neondatabase/autoscaling/pkg/util"
patchutil "github.com/neondatabase/autoscaling/pkg/util/patch"
)

// PluginProtocolVersion is the current version of the agent<->scheduler plugin in use by this
Expand Down Expand Up @@ -1353,12 +1354,12 @@ func (r *Runner) doVMUpdate(
r.recordResourceChange(current, target, r.global.metrics.neonvmRequestedChange)

// Make the NeonVM request
patches := []util.JSONPatch{{
Op: util.PatchReplace,
patches := []patchutil.Operation{{
Op: patchutil.OpReplace,
Path: "/spec/guest/cpus/use",
Value: target.VCPU.ToResourceQuantity(),
}, {
Op: util.PatchReplace,
Op: patchutil.OpReplace,
Path: "/spec/guest/memorySlots/use",
Value: target.Mem,
}}
Expand Down
31 changes: 0 additions & 31 deletions pkg/util/patch.go

This file was deleted.

52 changes: 52 additions & 0 deletions pkg/util/patch/patch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Construction of JSON patches. See https://jsonpatch.com/

package util

import (
"strings"
)

// OpKind is the kind of operation being performed in a single step
type OpKind string

const (
OpAdd OpKind = "add"
OpRemove OpKind = "remove"
OpReplace OpKind = "replace"
OpMove OpKind = "move"
OpCopy OpKind = "copy"
OpTest OpKind = "test"
)

type JSONPatch = []Operation

// Operation is a single step in the overall JSON patch
type Operation struct {
// Op is the kind of operation being performed in this step. See [OpKind] for more.
Op OpKind `json:"op"`
// Path is a [JSON pointer] to the target location of the operation.
//
// In general, nesting is separated by '/'s, with special characters escaped by '~'.
// [PathEscape] is provided to handle escaping, because it can get a little gnarly.
//
// As an example, if you want to add a field "foo" to the first element of an array,
// you'd use the path `/0/foo`. The jsonpatch website has more details (and clearer examples),
// refer there for more information: https://jsonpatch.com/#json-pointer
//
// [JSON pointer]: https://datatracker.ietf.org/doc/html/rfc6901/
Path string `json:"path"`
// From gives the source location for "copy" or "move" operations.
From string `json:"from,omitempty"`
// Value is the new value to use, for "add", "replace", or "test" operations.
Value any `json:"value,omitempty"`
}

var pathEscaper = strings.NewReplacer("~", "~0", "/", "~1")

// PathEscape escapes a string for use in a segement of the Path field of an Operation
//
// This is useful, for example, when using arbitrary strings as map keys (like Kubernetes labels or
// annotations).
func PathEscape(s string) string {
return pathEscaper.Replace(s)
}

0 comments on commit be3b8d1

Please sign in to comment.