Improve error handling in kyaml libraries

This commit is contained in:
Phillip Wittrock
2019-11-12 08:16:29 -08:00
parent 1bbd8b2c43
commit b473faccca
23 changed files with 483 additions and 96 deletions

View File

@@ -1,13 +1,10 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy
.PHONY: generate license fix vet fmt test lint tidy
GOPATH := $(shell go env GOPATH)
build:
go build -v -o $(GOPATH)/bin/kyaml .
all: generate license fix vet fmt test lint tidy
fix:
@@ -35,4 +32,3 @@ test:
vet:
go vet ./...

32
kyaml/errors/errors.go Normal file
View File

@@ -0,0 +1,32 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package errors provides libraries for working with the go-errors/errors library.
package errors
import (
"fmt"
goerrors "github.com/go-errors/errors"
)
// Wrap returns err wrapped in a go-error. If err is nil, returns nil.
func Wrap(err interface{}) error {
if err == nil {
return nil
}
return goerrors.Wrap(err, 1)
}
// WrapPrefixf returns err wrapped in a go-error with a message prefix. If err is nil, returns nil.
func WrapPrefixf(err interface{}, msg string, args ...interface{}) error {
if err == nil {
return nil
}
return goerrors.WrapPrefix(err, fmt.Sprintf(msg, args...), 1)
}
// Errorf returns a new go-error.
func Errorf(msg string, args ...interface{}) error {
return goerrors.Wrap(fmt.Errorf(msg, args...), 1)
}

View File

@@ -0,0 +1,57 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inpututil
import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type MapInputsEFn func(*yaml.RNode, yaml.ResourceMeta) error
// MapInputsE runs the function against each input Resource, providing the parsed metadata
func MapInputsE(inputs []*yaml.RNode, fn MapInputsEFn) error {
for i := range inputs {
meta, err := inputs[i].GetMeta()
if err != nil {
return errors.Wrap(err)
}
if err := fn(inputs[i], meta); err != nil {
return WrapErrorWithFile(err, meta)
}
}
return nil
}
type MapInputsFn func(*yaml.RNode, yaml.ResourceMeta) ([]*yaml.RNode, error)
// MapInputs runs the function against each input Resource, providing the parsed metadata
// and returning the aggregated result
func MapInputs(inputs []*yaml.RNode, fn MapInputsFn) ([]*yaml.RNode, error) {
var outputs []*yaml.RNode
for i := range inputs {
meta, err := inputs[i].GetMeta()
if err != nil {
return nil, errors.Wrap(err)
}
o, err := fn(inputs[i], meta)
if err != nil {
return nil, WrapErrorWithFile(err, meta)
}
outputs = append(outputs, o...)
}
return outputs, nil
}
// WrapErrorWithFile returns the original error wrapped with information about the file
// that the Resource was parsed from.
func WrapErrorWithFile(err error, meta yaml.ResourceMeta) error {
if err == nil {
return err
}
return errors.WrapPrefixf(err, "%s [%s]",
meta.Annotations[kioutil.PathAnnotation],
meta.Annotations[kioutil.IndexAnnotation])
}

View File

@@ -10,6 +10,7 @@ import (
"sort"
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -53,7 +54,7 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
rw.FunctionConfig = b.FunctionConfig
rw.WrappingApiVersion = b.WrappingApiVersion
rw.WrappingKind = b.WrappingKind
return val, err
return val, errors.Wrap(err)
}
func (rw *ByteReadWriter) Write(nodes []*yaml.RNode) error {
@@ -84,8 +85,16 @@ type ByteReader struct {
FunctionConfig *yaml.RNode
// DisableUnwrapping prevents Resources in Lists and ResourceLists from being unwrapped
DisableUnwrapping bool
// WrappingApiVersion is set by Read(), and is the apiVersion of the object that
// the read objects were originally wrapped in.
WrappingApiVersion string
WrappingKind string
// WrappingKind is set by Read(), and is the kind of the object that
// the read objects were originally wrapped in.
WrappingKind string
}
var _ Reader = &ByteReader{}
@@ -98,7 +107,7 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
input := &bytes.Buffer{}
_, err := io.Copy(input, r.Reader)
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
values := strings.Split(input.String(), "\n---\n")
@@ -110,7 +119,7 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
continue
}
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
if yaml.IsMissingOrNull(node) {
// empty value
@@ -118,12 +127,18 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
}
// ok if no metadata -- assume not an InputList
meta, _ := node.GetMeta()
meta, err := node.GetMeta()
if err != yaml.ErrMissingMetadata && err != nil {
return nil, errors.WrapPrefixf(err, "[%d]", i)
}
// the elements are wrapped in an InputList, unwrap them
// don't check apiVersion, we haven't standardized on the domain
if (meta.Kind == ResourceListKind || meta.Kind == "List") &&
if !r.DisableUnwrapping &&
len(values) == 1 && // Only unwrap if there is only 1 value
(meta.Kind == ResourceListKind || meta.Kind == "List") &&
node.Field("items") != nil {
r.WrappingKind = meta.Kind
r.WrappingApiVersion = meta.ApiVersion
@@ -166,7 +181,7 @@ func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, erro
return nil, io.EOF
}
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
if isEmptyDocument(node) {
@@ -191,7 +206,7 @@ func (r *ByteReader) decode(index int, decoder *yaml.Decoder) (*yaml.RNode, erro
for _, k := range keys {
_, err = n.Pipe(yaml.SetAnnotation(k, r.SetAnnotations[k]))
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
}
return yaml.NewRNode(node), nil

View File

@@ -6,6 +6,7 @@ package kio
import (
"io"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -46,7 +47,7 @@ var _ Writer = ByteWriter{}
func (w ByteWriter) Write(nodes []*yaml.RNode) error {
if w.Sort {
if err := kioutil.SortNodes(nodes); err != nil {
return err
return errors.Wrap(err)
}
}
@@ -58,13 +59,13 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
if !w.KeepReaderAnnotations {
_, err := nodes[i].Pipe(yaml.ClearAnnotation(kioutil.IndexAnnotation))
if err != nil {
return err
return errors.Wrap(err)
}
}
for _, a := range w.ClearAnnotations {
_, err := nodes[i].Pipe(yaml.ClearAnnotation(a))
if err != nil {
return err
return errors.Wrap(err)
}
}
@@ -72,11 +73,11 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
_, err := nodes[i].Pipe(yaml.Lookup("metadata"), yaml.FieldClearer{
Name: "annotations", IfEmpty: true})
if err != nil {
return err
return errors.Wrap(err)
}
_, err = nodes[i].Pipe(yaml.FieldClearer{Name: "metadata", IfEmpty: true})
if err != nil {
return err
return errors.Wrap(err)
}
if w.Style != 0 {
@@ -89,7 +90,7 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
for i := range nodes {
err := encoder.Encode(nodes[i].Document())
if err != nil {
return err
return errors.Wrap(err)
}
}
return nil
@@ -118,5 +119,5 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
for i := range nodes {
items.Content = append(items.Content, nodes[i].YNode())
}
return encoder.Encode(doc)
return errors.Wrap(encoder.Encode(doc))
}

View File

@@ -6,6 +6,7 @@
package kio
import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -85,7 +86,7 @@ func (p Pipeline) Execute() error {
for _, i := range p.Inputs {
nodes, err := i.Read()
if err != nil {
return err
return errors.Wrap(err)
}
result = append(result, nodes...)
}
@@ -100,14 +101,14 @@ func (p Pipeline) Execute() error {
op := p.Filters[i]
result, err = op.Filter(result)
if len(result) == 0 || err != nil {
return err
return errors.Wrap(err)
}
}
// write to the outputs
for _, o := range p.Outputs {
if err := o.Write(result); err != nil {
return err
return errors.Wrap(err)
}
}
return nil
@@ -119,7 +120,7 @@ func FilterAll(filter yaml.Filter) Filter {
for i := range nodes {
_, err := filter.Filter(nodes[i])
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
}
return nodes, nil

View File

@@ -8,6 +8,7 @@ import (
"sort"
"strconv"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -40,10 +41,10 @@ func ErrorIfMissingAnnotation(nodes []*yaml.RNode, keys ...AnnotationKey) error
for _, node := range nodes {
val, err := node.Pipe(yaml.GetAnnotation(key))
if err != nil {
return err
return errors.Wrap(err)
}
if val == nil {
return fmt.Errorf("missing package annotation %s", key)
return errors.Errorf("missing package annotation %s", key)
}
}
}
@@ -56,7 +57,7 @@ func Map(nodes []*yaml.RNode, fn func(*yaml.RNode) (*yaml.RNode, error)) ([]*yam
for i := range nodes {
n, err := fn(nodes[i])
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
if n != nil {
returnNodes = append(returnNodes, n)
@@ -71,11 +72,11 @@ func MapMeta(nodes []*yaml.RNode, fn func(*yaml.RNode, yaml.ResourceMeta) (*yaml
for i := range nodes {
meta, err := nodes[i].GetMeta()
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
n, err := fn(nodes[i], meta)
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
if n != nil {
returnNodes = append(returnNodes, n)
@@ -141,5 +142,5 @@ func SortNodes(nodes []*yaml.RNode) error {
// elements are equal
return false
})
return err
return errors.Wrap(err)
}

View File

@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -81,13 +82,13 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
SetAnnotations: r.SetAnnotations,
}.Read()
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
// keep track of all the files
if !r.NoDeleteFiles {
r.files, err = r.getFiles(nodes)
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
}
return nodes, nil
@@ -96,7 +97,7 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
func (r *LocalPackageReadWriter) Write(nodes []*yaml.RNode) error {
newFiles, err := r.getFiles(nodes)
if err != nil {
return err
return errors.Wrap(err)
}
var clear []string
for k := range r.SetAnnotations {
@@ -108,12 +109,12 @@ func (r *LocalPackageReadWriter) Write(nodes []*yaml.RNode) error {
KeepReaderAnnotations: r.KeepReaderAnnotations,
}.Write(nodes)
if err != nil {
return err
return errors.Wrap(err)
}
deleteFiles := r.files.Difference(newFiles)
for f := range deleteFiles {
if err = os.Remove(filepath.Join(r.PackagePath, f)); err != nil {
return err
return errors.Wrap(err)
}
}
return nil
@@ -124,7 +125,7 @@ func (r *LocalPackageReadWriter) getFiles(nodes []*yaml.RNode) (sets.String, err
for _, n := range nodes {
path, _, err := kioutil.GetFileAnnotations(n)
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
val.Insert(path)
}
@@ -182,7 +183,7 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
err := filepath.Walk(r.PackagePath, func(
path string, info os.FileInfo, err error) error {
if err != nil {
return err
return errors.Wrap(err)
}
// is this the user specified path?
@@ -213,13 +214,13 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
// to another location.
path, err = filepath.Rel(pathRelativeTo, path)
if err != nil {
return err
return errors.WrapPrefixf(err, pathRelativeTo)
}
r.initReaderAnnotations(path, info)
nodes, err := r.readFile(filepath.Join(pathRelativeTo, path), info)
if err != nil {
return err
return errors.WrapPrefixf(err, filepath.Join(pathRelativeTo, path))
}
operand = append(operand, nodes...)
return nil
@@ -235,6 +236,7 @@ func (r *LocalPackageReader) readFile(path string, info os.FileInfo) ([]*yaml.RN
}
defer f.Close()
rr := &ByteReader{
DisableUnwrapping: true,
Reader: f,
OmitReaderAnnotations: r.OmitReaderAnnotations,
SetAnnotations: r.SetAnnotations,
@@ -247,7 +249,7 @@ func (r *LocalPackageReader) shouldSkipFile(info os.FileInfo) (bool, error) {
// check if the files are in scope
for _, g := range r.MatchFilesGlob {
if match, err := filepath.Match(g, info.Name()); err != nil {
return false, err
return false, errors.Wrap(err)
} else if match {
return true, nil
}
@@ -276,7 +278,7 @@ func (r *LocalPackageReader) shouldSkipDir(path string) error {
if os.IsNotExist(err) {
return nil
} else if err != nil {
return err
return errors.Wrap(err)
}
if !r.IncludeSubpackages {
return filepath.SkipDir

View File

@@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -52,7 +53,7 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
}
for k := range outputFiles {
if err = kioutil.SortNodes(outputFiles[k]); err != nil {
return err
return errors.Wrap(err)
}
}
@@ -66,7 +67,7 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
outputPath := filepath.Join(r.PackagePath, path)
if st, err := os.Stat(outputPath); !os.IsNotExist(err) {
if err != nil {
return err
return errors.Wrap(err)
}
if st.IsDir() {
return fmt.Errorf("config.kubernetes.io/path cannot be a directory: %s", path)
@@ -75,7 +76,7 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
err = os.MkdirAll(filepath.Dir(outputPath), 0700)
if err != nil {
return err
return errors.Wrap(err)
}
}
@@ -84,12 +85,12 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
outputPath := filepath.Join(r.PackagePath, path)
err = os.MkdirAll(filepath.Dir(filepath.Join(r.PackagePath, path)), 0700)
if err != nil {
return err
return errors.Wrap(err)
}
f, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0600))
if err != nil {
return err
return errors.Wrap(err)
}
if err := func() error {
defer f.Close()
@@ -99,11 +100,11 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
ClearAnnotations: r.ClearAnnotations,
}
if err = w.Write(outputFiles[path]); err != nil {
return err
return errors.Wrap(err)
}
return nil
}(); err != nil {
return err
return errors.Wrap(err)
}
}
@@ -115,10 +116,10 @@ func (r LocalPackageWriter) errorIfMissingRequiredAnnotation(nodes []*yaml.RNode
for _, s := range requiredResourcePackageAnnotations {
key, err := nodes[i].Pipe(yaml.GetAnnotation(s))
if err != nil {
return err
return errors.Wrap(err)
}
if key == nil || key.YNode() == nil || key.YNode().Value == "" {
return fmt.Errorf(
return errors.Errorf(
"resources must be annotated with %s to be written to files", s)
}
}
@@ -135,13 +136,13 @@ func (r LocalPackageWriter) indexByFilePath(nodes []*yaml.RNode) (map[string][]*
value, err := node.Pipe(yaml.GetAnnotation(kioutil.PathAnnotation))
if err != nil {
// this should never happen if errorIfMissingRequiredAnnotation was run
return nil, err
return nil, errors.Wrap(err)
}
path := value.YNode().Value
outputFiles[path] = append(outputFiles[path], node)
if filepath.IsAbs(path) {
return nil, fmt.Errorf("package paths may not be absolute paths")
return nil, errors.Errorf("package paths may not be absolute paths")
}
if strings.Contains(filepath.Clean(path), "..") {
return nil, fmt.Errorf("resource must be written under package %s: %s",

View File

@@ -9,8 +9,8 @@ import (
"strings"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"gopkg.in/yaml.v3"
"sigs.k8s.io/kustomize/kyaml/errors"
)
// Append creates an ElementAppender
@@ -378,7 +378,7 @@ func (l PathGetter) doElem(rn *RNode, part string) (*RNode, error) {
var match *RNode
name, value, err := SplitIndexNameValue(part)
if err != nil {
return nil, err
return nil, errors.Wrap(err)
}
if !IsCreate(l.Create) {
return rn.Pipe(MatchElement(name, value))
@@ -537,6 +537,14 @@ func ErrorIfAnyInvalidAndNonNull(kind yaml.Kind, rn ...*RNode) error {
return nil
}
var nodeTypeIndex = map[yaml.Kind]string{
yaml.SequenceNode: "SequenceNode",
yaml.MappingNode: "MappingNode",
yaml.ScalarNode: "ScalarNode",
yaml.DocumentNode: "DocumentNode",
yaml.AliasNode: "AliasNode",
}
func ErrorIfInvalid(rn *RNode, kind yaml.Kind) error {
if rn == nil || rn.YNode() == nil || IsNull(rn) {
// node has no type, pass validation
@@ -548,12 +556,13 @@ func ErrorIfInvalid(rn *RNode, kind yaml.Kind) error {
return errors.Errorf(
"wrong Node Kind for %s expected: %v was %v: value: {%s}",
strings.Join(rn.FieldPath(), "."),
kind, rn.YNode().Kind, strings.TrimSpace(s))
nodeTypeIndex[kind], nodeTypeIndex[rn.YNode().Kind], strings.TrimSpace(s))
}
if kind == yaml.MappingNode {
if len(rn.YNode().Content)%2 != 0 {
return fmt.Errorf("yaml MappingNodes must have even length contents: %v", spew.Sdump(rn))
return errors.Errorf(
"yaml MappingNodes must have even length contents: %v", spew.Sdump(rn))
}
}

View File

@@ -5,11 +5,12 @@ package yaml
import (
"bytes"
"errors"
"fmt"
"reflect"
"strings"
"gopkg.in/yaml.v3"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/sets"
)
@@ -240,7 +241,7 @@ type ObjectMeta struct {
Annotations map[string]string `yaml:"annotations,omitempty"`
}
var ErrMissingMetadata = errors.New("missing Resource metadata")
var ErrMissingMetadata = fmt.Errorf("missing Resource metadata")
// GetMeta returns the ResourceMeta for a RNode
func (rn *RNode) GetMeta() (ResourceMeta, error) {
@@ -248,15 +249,15 @@ func (rn *RNode) GetMeta() (ResourceMeta, error) {
b := &bytes.Buffer{}
e := NewEncoder(b)
if err := e.Encode(rn.YNode()); err != nil {
return m, err
return m, errors.Wrap(err)
}
if err := e.Close(); err != nil {
return m, err
return m, errors.Wrap(err)
}
d := yaml.NewDecoder(b)
d.KnownFields(false) // only want to parse the metadata
if err := d.Decode(&m); err != nil {
return m, err
return m, errors.Wrap(err)
}
if reflect.DeepEqual(m, ResourceMeta{}) {
return m, ErrMissingMetadata
@@ -279,8 +280,8 @@ func (rn *RNode) Pipe(functions ...Filter) (*RNode, error) {
return nil, nil
}
var err error
var v *RNode
var err error
if rn.value != nil && rn.value.Kind == yaml.DocumentNode {
// the first node may be a DocumentNode containing a single MappingNode
v = &RNode{value: rn.value.Content[0]}
@@ -292,7 +293,7 @@ func (rn *RNode) Pipe(functions ...Filter) (*RNode, error) {
for _, c := range functions {
v, err = c.Filter(v)
if err != nil || v == nil {
return v, err
return v, errors.Wrap(err)
}
}
return v, err
@@ -302,7 +303,7 @@ func (rn *RNode) Pipe(functions ...Filter) (*RNode, error) {
// Useful for directly returning the Pipe error value from functions.
func (rn *RNode) PipeE(functions ...Filter) error {
_, err := rn.Pipe(functions...)
return err
return errors.Wrap(err)
}
// Document returns the Node RNode for the value. Does not unwrap the node if it is a
@@ -371,7 +372,7 @@ func String(node *yaml.Node, opts ...string) (string, error) {
if optsSet.Has(Trim) {
val = strings.TrimSpace(val)
}
return val, err
return val, errors.Wrap(err)
}
// String returns string representation of the RNode
@@ -403,7 +404,7 @@ func (rn *RNode) Content() []*yaml.Node {
// Returns an error for non-MappingNodes.
func (rn *RNode) Fields() ([]string, error) {
if err := ErrorIfInvalid(rn, yaml.MappingNode); err != nil {
return nil, err
return nil, errors.Wrap(err)
}
var fields []string
for i := 0; i < len(rn.Content()); i += 2 {
@@ -433,13 +434,13 @@ func (rn *RNode) VisitFields(fn func(node *MapNode) error) error {
// get the list of srcFieldNames
srcFieldNames, err := rn.Fields()
if err != nil {
return err
return errors.Wrap(err)
}
// visit each field
for _, fieldName := range srcFieldNames {
if err := fn(rn.Field(fieldName)); err != nil {
return err
return errors.Wrap(err)
}
}
return nil
@@ -449,7 +450,7 @@ func (rn *RNode) VisitFields(fn func(node *MapNode) error) error {
// Returns an error for non-SequenceNodes.
func (rn *RNode) Elements() ([]*RNode, error) {
if err := ErrorIfInvalid(rn, yaml.SequenceNode); err != nil {
return nil, err
return nil, errors.Wrap(err)
}
var elements []*RNode
for i := 0; i < len(rn.Content()); i++ {
@@ -463,7 +464,7 @@ func (rn *RNode) Elements() ([]*RNode, error) {
// Returns error for non-SequenceNodes.
func (rn *RNode) ElementValues(key string) ([]string, error) {
if err := ErrorIfInvalid(rn, yaml.SequenceNode); err != nil {
return nil, err
return nil, errors.Wrap(err)
}
var elements []string
for i := 0; i < len(rn.Content()); i++ {
@@ -493,12 +494,12 @@ func (rn *RNode) Element(key, value string) *RNode {
func (rn *RNode) VisitElements(fn func(node *RNode) error) error {
elements, err := rn.Elements()
if err != nil {
return err
return errors.Wrap(err)
}
for i := range elements {
if err := fn(elements[i]); err != nil {
return err
return errors.Wrap(err)
}
}
return nil