feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
492
vendor/github.com/open-policy-agent/opa/topdown/json.go
generated
vendored
492
vendor/github.com/open-policy-agent/opa/topdown/json.go
generated
vendored
@@ -11,10 +11,11 @@ import (
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/topdown/builtins"
|
||||
|
||||
"github.com/open-policy-agent/opa/internal/edittree"
|
||||
)
|
||||
|
||||
func builtinJSONRemove(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
|
||||
|
||||
// Expect an object and a string or array/set of strings
|
||||
_, err := builtins.ObjectOperand(operands[0].Value, 1)
|
||||
if err != nil {
|
||||
@@ -116,7 +117,6 @@ func jsonRemove(a *ast.Term, b *ast.Term) (*ast.Term, error) {
|
||||
}
|
||||
|
||||
func builtinJSONFilter(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
|
||||
|
||||
// Ensure we have the right parameters, expect an object and a string or array/set of strings
|
||||
obj, err := builtins.ObjectOperand(operands[0].Value, 1)
|
||||
if err != nil {
|
||||
@@ -196,7 +196,6 @@ func parsePath(path *ast.Term) (ast.Ref, error) {
|
||||
}
|
||||
|
||||
func pathsToObject(paths []ast.Ref) ast.Object {
|
||||
|
||||
root := ast.NewObject()
|
||||
|
||||
for _, path := range paths {
|
||||
@@ -239,288 +238,147 @@ func pathsToObject(paths []ast.Ref) ast.Object {
|
||||
return root
|
||||
}
|
||||
|
||||
// toIndex tries to convert path elements (that may be strings) into indices into
|
||||
// an array.
|
||||
func toIndex(arr *ast.Array, term *ast.Term) (int, error) {
|
||||
i := 0
|
||||
type jsonPatch struct {
|
||||
op string
|
||||
path *ast.Term
|
||||
from *ast.Term
|
||||
value *ast.Term
|
||||
}
|
||||
|
||||
func getPatch(o ast.Object) (jsonPatch, error) {
|
||||
validOps := map[string]struct{}{"add": {}, "remove": {}, "replace": {}, "move": {}, "copy": {}, "test": {}}
|
||||
var out jsonPatch
|
||||
var ok bool
|
||||
switch v := term.Value.(type) {
|
||||
case ast.Number:
|
||||
if i, ok = v.Int(); !ok {
|
||||
return 0, fmt.Errorf("Invalid number type for indexing")
|
||||
getAttribute := func(attr string) (*ast.Term, error) {
|
||||
if term := o.Get(ast.StringTerm(attr)); term != nil {
|
||||
return term, nil
|
||||
}
|
||||
case ast.String:
|
||||
if v == "-" {
|
||||
return arr.Len(), nil
|
||||
}
|
||||
num := ast.Number(v)
|
||||
if i, ok = num.Int(); !ok {
|
||||
return 0, fmt.Errorf("Invalid string for indexing")
|
||||
}
|
||||
if v != "0" && strings.HasPrefix(string(v), "0") {
|
||||
return 0, fmt.Errorf("Leading zeros are not allowed in JSON paths")
|
||||
}
|
||||
default:
|
||||
return 0, fmt.Errorf("Invalid type for indexing")
|
||||
|
||||
return nil, fmt.Errorf("missing '%s' attribute", attr)
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// patchWorkerris a worker that modifies a direct child of a term located
|
||||
// at the given key. It returns the new term, and optionally a result that
|
||||
// is passed back to the caller.
|
||||
type patchWorker = func(parent, key *ast.Term) (updated, result *ast.Term)
|
||||
|
||||
func jsonPatchTraverse(
|
||||
target *ast.Term,
|
||||
path ast.Ref,
|
||||
worker patchWorker,
|
||||
) (*ast.Term, *ast.Term) {
|
||||
if len(path) < 1 {
|
||||
return nil, nil
|
||||
opTerm, err := getAttribute("op")
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
op, ok := opTerm.Value.(ast.String)
|
||||
if !ok {
|
||||
return out, fmt.Errorf("attribute 'op' must be a string")
|
||||
}
|
||||
out.op = string(op)
|
||||
if _, found := validOps[out.op]; !found {
|
||||
out.op = ""
|
||||
return out, fmt.Errorf("unrecognized op '%s'", string(op))
|
||||
}
|
||||
|
||||
key := path[0]
|
||||
if len(path) == 1 {
|
||||
return worker(target, key)
|
||||
pathTerm, err := getAttribute("path")
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.path = pathTerm
|
||||
|
||||
success := false
|
||||
var updated, result *ast.Term
|
||||
switch parent := target.Value.(type) {
|
||||
case ast.Object:
|
||||
obj := ast.NewObject()
|
||||
parent.Foreach(func(k, v *ast.Term) {
|
||||
if k.Equal(key) {
|
||||
if v, result = jsonPatchTraverse(v, path[1:], worker); v != nil {
|
||||
obj.Insert(k, v)
|
||||
success = true
|
||||
}
|
||||
} else {
|
||||
obj.Insert(k, v)
|
||||
}
|
||||
})
|
||||
updated = ast.NewTerm(obj)
|
||||
|
||||
case *ast.Array:
|
||||
idx, err := toIndex(parent, key)
|
||||
// Only fetch the "from" parameter for move/copy ops.
|
||||
switch out.op {
|
||||
case "move", "copy":
|
||||
fromTerm, err := getAttribute("from")
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
return out, err
|
||||
}
|
||||
arr := ast.NewArray()
|
||||
for i := 0; i < parent.Len(); i++ {
|
||||
v := parent.Elem(i)
|
||||
if idx == i {
|
||||
if v, result = jsonPatchTraverse(v, path[1:], worker); v != nil {
|
||||
arr = arr.Append(v)
|
||||
success = true
|
||||
}
|
||||
} else {
|
||||
arr = arr.Append(v)
|
||||
out.from = fromTerm
|
||||
}
|
||||
|
||||
// Only fetch the "value" parameter for add/replace/test ops.
|
||||
switch out.op {
|
||||
case "add", "replace", "test":
|
||||
valueTerm, err := getAttribute("value")
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.value = valueTerm
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func applyPatches(source *ast.Term, operations *ast.Array) (*ast.Term, error) {
|
||||
et := edittree.NewEditTree(source)
|
||||
for i := 0; i < operations.Len(); i++ {
|
||||
object, ok := operations.Elem(i).Value.(ast.Object)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("must be an array of JSON-Patch objects, but at least one element is not an object")
|
||||
}
|
||||
patch, err := getPatch(object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := parsePath(patch.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch patch.op {
|
||||
case "add":
|
||||
_, err = et.InsertAtPath(path, patch.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "remove":
|
||||
_, err = et.DeleteAtPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "replace":
|
||||
_, err = et.DeleteAtPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = et.InsertAtPath(path, patch.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "move":
|
||||
from, err := parsePath(patch.from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chunk, err := et.RenderAtPath(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = et.DeleteAtPath(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = et.InsertAtPath(path, chunk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "copy":
|
||||
from, err := parsePath(patch.from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chunk, err := et.RenderAtPath(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = et.InsertAtPath(path, chunk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "test":
|
||||
chunk, err := et.RenderAtPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !chunk.Equal(patch.value) {
|
||||
return nil, fmt.Errorf("value from EditTree != patch value.\n\nExpected: %v\n\nFound: %v", patch.value, chunk)
|
||||
}
|
||||
}
|
||||
updated = ast.NewTerm(arr)
|
||||
|
||||
case ast.Set:
|
||||
set := ast.NewSet()
|
||||
parent.Foreach(func(k *ast.Term) {
|
||||
if k.Equal(key) {
|
||||
if k, result = jsonPatchTraverse(k, path[1:], worker); k != nil {
|
||||
set.Add(k)
|
||||
success = true
|
||||
}
|
||||
} else {
|
||||
set.Add(k)
|
||||
}
|
||||
})
|
||||
updated = ast.NewTerm(set)
|
||||
}
|
||||
|
||||
if success {
|
||||
return updated, result
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// jsonPatchGet goes one step further than jsonPatchTraverse and returns the
|
||||
// term at the location specified by the path. It is used in functions
|
||||
// where we want to read a value but not manipulate its parent: for example
|
||||
// jsonPatchTest and jsonPatchCopy.
|
||||
//
|
||||
// Because it uses jsonPatchTraverse, it makes shallow copies of the objects
|
||||
// along the path. We could possibly add a signaling mechanism that we didn't
|
||||
// make any changes to avoid this.
|
||||
func jsonPatchGet(target *ast.Term, path ast.Ref) *ast.Term {
|
||||
// Special case: get entire document.
|
||||
if len(path) == 0 {
|
||||
return target
|
||||
}
|
||||
|
||||
_, result := jsonPatchTraverse(target, path, func(parent, key *ast.Term) (*ast.Term, *ast.Term) {
|
||||
switch v := parent.Value.(type) {
|
||||
case ast.Object:
|
||||
return parent, v.Get(key)
|
||||
case *ast.Array:
|
||||
i, err := toIndex(v, key)
|
||||
if err == nil {
|
||||
return parent, v.Elem(i)
|
||||
}
|
||||
case ast.Set:
|
||||
if v.Contains(key) {
|
||||
return parent, key
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func jsonPatchAdd(target *ast.Term, path ast.Ref, value *ast.Term) *ast.Term {
|
||||
// Special case: replacing root document.
|
||||
if len(path) == 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
target, _ = jsonPatchTraverse(target, path, func(parent *ast.Term, key *ast.Term) (*ast.Term, *ast.Term) {
|
||||
switch original := parent.Value.(type) {
|
||||
case ast.Object:
|
||||
obj := ast.NewObject()
|
||||
original.Foreach(func(k, v *ast.Term) {
|
||||
obj.Insert(k, v)
|
||||
})
|
||||
obj.Insert(key, value)
|
||||
return ast.NewTerm(obj), nil
|
||||
case *ast.Array:
|
||||
idx, err := toIndex(original, key)
|
||||
if err != nil || idx < 0 || idx > original.Len() {
|
||||
return nil, nil
|
||||
}
|
||||
arr := ast.NewArray()
|
||||
for i := 0; i < idx; i++ {
|
||||
arr = arr.Append(original.Elem(i))
|
||||
}
|
||||
arr = arr.Append(value)
|
||||
for i := idx; i < original.Len(); i++ {
|
||||
arr = arr.Append(original.Elem(i))
|
||||
}
|
||||
return ast.NewTerm(arr), nil
|
||||
case ast.Set:
|
||||
if !key.Equal(value) {
|
||||
return nil, nil
|
||||
}
|
||||
set := ast.NewSet()
|
||||
original.Foreach(func(k *ast.Term) {
|
||||
set.Add(k)
|
||||
})
|
||||
set.Add(key)
|
||||
return ast.NewTerm(set), nil
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
func jsonPatchRemove(target *ast.Term, path ast.Ref) (*ast.Term, *ast.Term) {
|
||||
// Special case: replacing root document.
|
||||
if len(path) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
target, removed := jsonPatchTraverse(target, path, func(parent *ast.Term, key *ast.Term) (*ast.Term, *ast.Term) {
|
||||
var removed *ast.Term
|
||||
switch original := parent.Value.(type) {
|
||||
case ast.Object:
|
||||
obj := ast.NewObject()
|
||||
original.Foreach(func(k, v *ast.Term) {
|
||||
if k.Equal(key) {
|
||||
removed = v
|
||||
} else {
|
||||
obj.Insert(k, v)
|
||||
}
|
||||
})
|
||||
return ast.NewTerm(obj), removed
|
||||
case *ast.Array:
|
||||
idx, err := toIndex(original, key)
|
||||
if err != nil || idx < 0 || idx >= original.Len() {
|
||||
return nil, nil
|
||||
}
|
||||
arr := ast.NewArray()
|
||||
for i := 0; i < idx; i++ {
|
||||
arr = arr.Append(original.Elem(i))
|
||||
}
|
||||
removed = original.Elem(idx)
|
||||
for i := idx + 1; i < original.Len(); i++ {
|
||||
arr = arr.Append(original.Elem(i))
|
||||
}
|
||||
return ast.NewTerm(arr), removed
|
||||
case ast.Set:
|
||||
set := ast.NewSet()
|
||||
original.Foreach(func(k *ast.Term) {
|
||||
if k.Equal(key) {
|
||||
removed = k
|
||||
} else {
|
||||
set.Add(k)
|
||||
}
|
||||
})
|
||||
return ast.NewTerm(set), removed
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
if target != nil && removed != nil {
|
||||
return target, removed
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func jsonPatchReplace(target *ast.Term, path ast.Ref, value *ast.Term) *ast.Term {
|
||||
// Special case: replacing the whole document.
|
||||
if len(path) == 0 {
|
||||
return value
|
||||
}
|
||||
|
||||
// Replace is specified as `remove` followed by `add`.
|
||||
if target, _ = jsonPatchRemove(target, path); target == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return jsonPatchAdd(target, path, value)
|
||||
}
|
||||
|
||||
func jsonPatchMove(target *ast.Term, path ast.Ref, from ast.Ref) *ast.Term {
|
||||
// Move is specified as `remove` followed by `add`.
|
||||
target, removed := jsonPatchRemove(target, from)
|
||||
if target == nil || removed == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return jsonPatchAdd(target, path, removed)
|
||||
}
|
||||
|
||||
func jsonPatchCopy(target *ast.Term, path ast.Ref, from ast.Ref) *ast.Term {
|
||||
value := jsonPatchGet(target, from)
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return jsonPatchAdd(target, path, value)
|
||||
}
|
||||
|
||||
func jsonPatchTest(target *ast.Term, path ast.Ref, value *ast.Term) *ast.Term {
|
||||
actual := jsonPatchGet(target, path)
|
||||
if actual == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if actual.Equal(value) {
|
||||
return target
|
||||
}
|
||||
|
||||
return nil
|
||||
final := et.Render()
|
||||
// TODO: Nil check here?
|
||||
return final, nil
|
||||
}
|
||||
|
||||
func builtinJSONPatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
|
||||
@@ -533,93 +391,11 @@ func builtinJSONPatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter
|
||||
return err
|
||||
}
|
||||
|
||||
// Apply operations one by one.
|
||||
for i := 0; i < operations.Len(); i++ {
|
||||
if object, ok := operations.Elem(i).Value.(ast.Object); ok {
|
||||
getAttribute := func(attr string) (*ast.Term, error) {
|
||||
if term := object.Get(ast.StringTerm(attr)); term != nil {
|
||||
return term, nil
|
||||
}
|
||||
|
||||
return nil, builtins.NewOperandErr(2, fmt.Sprintf("patch is missing '%s' attribute", attr))
|
||||
}
|
||||
|
||||
getPathAttribute := func(attr string) (ast.Ref, error) {
|
||||
term, err := getAttribute(attr)
|
||||
if err != nil {
|
||||
return ast.Ref{}, err
|
||||
}
|
||||
path, err := parsePath(term)
|
||||
if err != nil {
|
||||
return ast.Ref{}, err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// Parse operation.
|
||||
opTerm, err := getAttribute("op")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
op, ok := opTerm.Value.(ast.String)
|
||||
if !ok {
|
||||
return builtins.NewOperandErr(2, "patch attribute 'op' must be a string")
|
||||
}
|
||||
|
||||
// Parse path.
|
||||
path, err := getPathAttribute("path")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch op {
|
||||
case "add":
|
||||
value, err := getAttribute("value")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target = jsonPatchAdd(target, path, value)
|
||||
case "remove":
|
||||
target, _ = jsonPatchRemove(target, path)
|
||||
case "replace":
|
||||
value, err := getAttribute("value")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target = jsonPatchReplace(target, path, value)
|
||||
case "move":
|
||||
from, err := getPathAttribute("from")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target = jsonPatchMove(target, path, from)
|
||||
case "copy":
|
||||
from, err := getPathAttribute("from")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target = jsonPatchCopy(target, path, from)
|
||||
case "test":
|
||||
value, err := getAttribute("value")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target = jsonPatchTest(target, path, value)
|
||||
default:
|
||||
return builtins.NewOperandErr(2, "must be an array of JSON-Patch objects")
|
||||
}
|
||||
} else {
|
||||
return builtins.NewOperandErr(2, "must be an array of JSON-Patch objects")
|
||||
}
|
||||
|
||||
// JSON patches should work atomically; and if one of them fails,
|
||||
// we should not try to continue.
|
||||
if target == nil {
|
||||
return nil
|
||||
}
|
||||
patched, err := applyPatches(target, operations)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return iter(target)
|
||||
return iter(patched)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
Reference in New Issue
Block a user