github.com/alex123012/deckhouse-controller-tools@v0.0.0-20230510090815-d594daf1af8c/pkg/schemapatcher/internal/yaml/nested.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package yaml 18 19 import ( 20 "fmt" 21 22 "gopkg.in/yaml.v3" 23 ) 24 25 // ValueInMapping finds the value node with the corresponding string key 26 // in the given mapping node. If the given node is not a mapping, an 27 // error will be returned. 28 func ValueInMapping(root *yaml.Node, key string) (*yaml.Node, error) { 29 if root.Kind != yaml.MappingNode { 30 return nil, fmt.Errorf("unexpected non-mapping node") 31 } 32 33 for i := 0; i < len(root.Content)/2; i++ { 34 keyNode := root.Content[i*2] 35 if keyNode.Value == key { 36 return root.Content[i*2+1], nil 37 } 38 } 39 return nil, nil 40 } 41 42 // asCloseAsPossible goes as deep on the given path as possible, returning the 43 // last node that existed from the given path in the given tree of mapping 44 // nodes, as well as the rest of the path that could not be fetched, if any. 45 func asCloseAsPossible(root *yaml.Node, path ...string) (*yaml.Node, []string, error) { 46 if root == nil { 47 return nil, path, nil 48 } 49 if root.Kind == yaml.DocumentNode && len(root.Content) > 0 { 50 root = root.Content[0] 51 } 52 53 currNode := root 54 for ; len(path) > 0; path = path[1:] { 55 if currNode.Kind != yaml.MappingNode { 56 return nil, nil, fmt.Errorf("unexpected non-mapping (%v) before path %v", currNode.Kind, path) 57 } 58 59 nextNode, err := ValueInMapping(currNode, path[0]) 60 if err != nil { 61 return nil, nil, fmt.Errorf("unable to get next node in path %v: %w", path, err) 62 } 63 64 if nextNode == nil { 65 // we're as close as possible 66 break 67 } 68 69 currNode = nextNode 70 } 71 72 return currNode, path, nil 73 } 74 75 // GetNode gets the node at the given path in the given sequence of mapping 76 // nodes, or, if it doesn't exist, returning false. 77 func GetNode(root *yaml.Node, path ...string) (*yaml.Node, bool, error) { 78 resNode, restPath, err := asCloseAsPossible(root, path...) 79 if err != nil { 80 return nil, false, err 81 } 82 // more path means the node didn't exist 83 if len(restPath) != 0 { 84 return nil, false, nil 85 } 86 return resNode, true, nil 87 }