mirror of
https://github.com/getsops/sops.git
synced 2026-02-06 06:45:18 +01:00
It's been around 9 months since our last vendor update. This is also needed for some new features being worked on for sops workspace. Additionally, this PR regenerates the kms mocks.
481 lines
15 KiB
Go
481 lines
15 KiB
Go
// Copyright 2016 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package fields provides a view of the fields of a struct that follows the Go
|
|
// rules, amended to consider tags and case insensitivity.
|
|
//
|
|
// Usage
|
|
//
|
|
// First define a function that interprets tags:
|
|
//
|
|
// func parseTag(st reflect.StructTag) (name string, keep bool, other interface{}, err error) { ... }
|
|
//
|
|
// The function's return values describe whether to ignore the field
|
|
// completely or provide an alternate name, as well as other data from the
|
|
// parse that is stored to avoid re-parsing.
|
|
//
|
|
// Then define a function to validate the type:
|
|
//
|
|
// func validate(t reflect.Type) error { ... }
|
|
//
|
|
// Then, if necessary, define a function to specify leaf types - types
|
|
// which should be considered one field and not be recursed into:
|
|
//
|
|
// func isLeafType(t reflect.Type) bool { ... }
|
|
//
|
|
// eg:
|
|
//
|
|
// func isLeafType(t reflect.Type) bool {
|
|
// return t == reflect.TypeOf(time.Time{})
|
|
// }
|
|
//
|
|
// Next, construct a Cache, passing your functions. As its name suggests, a
|
|
// Cache remembers validation and field information for a type, so subsequent
|
|
// calls with the same type are very fast.
|
|
//
|
|
// cache := fields.NewCache(parseTag, validate, isLeafType)
|
|
//
|
|
// To get the fields of a struct type as determined by the above rules, call
|
|
// the Fields method:
|
|
//
|
|
// fields, err := cache.Fields(reflect.TypeOf(MyStruct{}))
|
|
//
|
|
// The return value can be treated as a slice of Fields.
|
|
//
|
|
// Given a string, such as a key or column name obtained during unmarshalling,
|
|
// call Match on the list of fields to find a field whose name is the best
|
|
// match:
|
|
//
|
|
// field := fields.Match(name)
|
|
//
|
|
// Match looks for an exact match first, then falls back to a case-insensitive
|
|
// comparison.
|
|
package fields
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// A Field records information about a struct field.
|
|
type Field struct {
|
|
Name string // effective field name
|
|
NameFromTag bool // did Name come from a tag?
|
|
Type reflect.Type // field type
|
|
Index []int // index sequence, for reflect.Value.FieldByIndex
|
|
ParsedTag interface{} // third return value of the parseTag function
|
|
|
|
nameBytes []byte
|
|
equalFold func(s, t []byte) bool
|
|
}
|
|
|
|
// ParseTagFunc is a function that accepts a struct tag and returns four values: an alternative name for the field
|
|
// extracted from the tag, a boolean saying whether to keep the field or ignore it, additional data that is stored
|
|
// with the field information to avoid having to parse the tag again, and an error.
|
|
type ParseTagFunc func(reflect.StructTag) (name string, keep bool, other interface{}, err error)
|
|
|
|
// ValidateFunc is a function that accepts a reflect.Type and returns an error if the struct type is invalid in any
|
|
// way.
|
|
type ValidateFunc func(reflect.Type) error
|
|
|
|
// LeafTypesFunc is a function that accepts a reflect.Type and returns true if the struct type a leaf, or false if not.
|
|
// TODO(deklerk): is this description accurate?
|
|
type LeafTypesFunc func(reflect.Type) bool
|
|
|
|
// A Cache records information about the fields of struct types.
|
|
//
|
|
// A Cache is safe for use by multiple goroutines.
|
|
type Cache struct {
|
|
parseTag ParseTagFunc
|
|
validate ValidateFunc
|
|
leafTypes LeafTypesFunc
|
|
cache sync.Map // from reflect.Type to cacheValue
|
|
}
|
|
|
|
// NewCache constructs a Cache.
|
|
//
|
|
// Its first argument should be a function that accepts
|
|
// a struct tag and returns four values: an alternative name for the field
|
|
// extracted from the tag, a boolean saying whether to keep the field or ignore
|
|
// it, additional data that is stored with the field information to avoid
|
|
// having to parse the tag again, and an error.
|
|
//
|
|
// Its second argument should be a function that accepts a reflect.Type and
|
|
// returns an error if the struct type is invalid in any way. For example, it
|
|
// may check that all of the struct field tags are valid, or that all fields
|
|
// are of an appropriate type.
|
|
func NewCache(parseTag ParseTagFunc, validate ValidateFunc, leafTypes LeafTypesFunc) *Cache {
|
|
if parseTag == nil {
|
|
parseTag = func(reflect.StructTag) (string, bool, interface{}, error) {
|
|
return "", true, nil, nil
|
|
}
|
|
}
|
|
if validate == nil {
|
|
validate = func(reflect.Type) error {
|
|
return nil
|
|
}
|
|
}
|
|
if leafTypes == nil {
|
|
leafTypes = func(reflect.Type) bool {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return &Cache{
|
|
parseTag: parseTag,
|
|
validate: validate,
|
|
leafTypes: leafTypes,
|
|
}
|
|
}
|
|
|
|
// A fieldScan represents an item on the fieldByNameFunc scan work list.
|
|
type fieldScan struct {
|
|
typ reflect.Type
|
|
index []int
|
|
}
|
|
|
|
// Fields returns all the exported fields of t, which must be a struct type. It
|
|
// follows the standard Go rules for embedded fields, modified by the presence
|
|
// of tags. The result is sorted lexicographically by index.
|
|
//
|
|
// These rules apply in the absence of tags:
|
|
// Anonymous struct fields are treated as if their inner exported fields were
|
|
// fields in the outer struct (embedding). The result includes all fields that
|
|
// aren't shadowed by fields at higher level of embedding. If more than one
|
|
// field with the same name exists at the same level of embedding, it is
|
|
// excluded. An anonymous field that is not of struct type is treated as having
|
|
// its type as its name.
|
|
//
|
|
// Tags modify these rules as follows:
|
|
// A field's tag is used as its name.
|
|
// An anonymous struct field with a name given in its tag is treated as
|
|
// a field having that name, rather than an embedded struct (the struct's
|
|
// fields will not be returned).
|
|
// If more than one field with the same name exists at the same level of embedding,
|
|
// but exactly one of them is tagged, then the tagged field is reported and the others
|
|
// are ignored.
|
|
func (c *Cache) Fields(t reflect.Type) (List, error) {
|
|
if t.Kind() != reflect.Struct {
|
|
panic("fields: Fields of non-struct type")
|
|
}
|
|
return c.cachedTypeFields(t)
|
|
}
|
|
|
|
// A List is a list of Fields.
|
|
type List []Field
|
|
|
|
// Match returns the field in the list whose name best matches the supplied
|
|
// name, nor nil if no field does. If there is a field with the exact name, it
|
|
// is returned. Otherwise the first field (sorted by index) whose name matches
|
|
// case-insensitively is returned.
|
|
func (l List) Match(name string) *Field {
|
|
return l.MatchBytes([]byte(name))
|
|
}
|
|
|
|
// MatchBytes is identical to Match, except that the argument is a byte slice.
|
|
func (l List) MatchBytes(name []byte) *Field {
|
|
var f *Field
|
|
for i := range l {
|
|
ff := &l[i]
|
|
if bytes.Equal(ff.nameBytes, name) {
|
|
return ff
|
|
}
|
|
if f == nil && ff.equalFold(ff.nameBytes, name) {
|
|
f = ff
|
|
}
|
|
}
|
|
return f
|
|
}
|
|
|
|
type cacheValue struct {
|
|
fields List
|
|
err error
|
|
}
|
|
|
|
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
// This code has been copied and modified from
|
|
// https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/encode.go.
|
|
func (c *Cache) cachedTypeFields(t reflect.Type) (List, error) {
|
|
var cv cacheValue
|
|
x, ok := c.cache.Load(t)
|
|
if ok {
|
|
cv = x.(cacheValue)
|
|
} else {
|
|
if err := c.validate(t); err != nil {
|
|
cv = cacheValue{nil, err}
|
|
} else {
|
|
f, err := c.typeFields(t)
|
|
cv = cacheValue{List(f), err}
|
|
}
|
|
c.cache.Store(t, cv)
|
|
}
|
|
return cv.fields, cv.err
|
|
}
|
|
|
|
func (c *Cache) typeFields(t reflect.Type) ([]Field, error) {
|
|
fields, err := c.listFields(t)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sort.Sort(byName(fields))
|
|
// Delete all fields that are hidden by the Go rules for embedded fields.
|
|
|
|
// The fields are sorted in primary order of name, secondary order of field
|
|
// index length. So the first field with a given name is the dominant one.
|
|
var out []Field
|
|
for advance, i := 0, 0; i < len(fields); i += advance {
|
|
// One iteration per name.
|
|
// Find the sequence of fields with the name of this first field.
|
|
fi := fields[i]
|
|
name := fi.Name
|
|
for advance = 1; i+advance < len(fields); advance++ {
|
|
fj := fields[i+advance]
|
|
if fj.Name != name {
|
|
break
|
|
}
|
|
}
|
|
// Find the dominant field, if any, out of all fields that have the same name.
|
|
dominant, ok := dominantField(fields[i : i+advance])
|
|
if ok {
|
|
out = append(out, dominant)
|
|
}
|
|
}
|
|
sort.Sort(byIndex(out))
|
|
return out, nil
|
|
}
|
|
|
|
func (c *Cache) listFields(t reflect.Type) ([]Field, error) {
|
|
// This uses the same condition that the Go language does: there must be a unique instance
|
|
// of the match at a given depth level. If there are multiple instances of a match at the
|
|
// same depth, they annihilate each other and inhibit any possible match at a lower level.
|
|
// The algorithm is breadth first search, one depth level at a time.
|
|
|
|
// The current and next slices are work queues:
|
|
// current lists the fields to visit on this depth level,
|
|
// and next lists the fields on the next lower level.
|
|
current := []fieldScan{}
|
|
next := []fieldScan{{typ: t}}
|
|
|
|
// nextCount records the number of times an embedded type has been
|
|
// encountered and considered for queueing in the 'next' slice.
|
|
// We only queue the first one, but we increment the count on each.
|
|
// If a struct type T can be reached more than once at a given depth level,
|
|
// then it annihilates itself and need not be considered at all when we
|
|
// process that next depth level.
|
|
var nextCount map[reflect.Type]int
|
|
|
|
// visited records the structs that have been considered already.
|
|
// Embedded pointer fields can create cycles in the graph of
|
|
// reachable embedded types; visited avoids following those cycles.
|
|
// It also avoids duplicated effort: if we didn't find the field in an
|
|
// embedded type T at level 2, we won't find it in one at level 4 either.
|
|
visited := map[reflect.Type]bool{}
|
|
|
|
var fields []Field // Fields found.
|
|
|
|
for len(next) > 0 {
|
|
current, next = next, current[:0]
|
|
count := nextCount
|
|
nextCount = nil
|
|
|
|
// Process all the fields at this depth, now listed in 'current'.
|
|
// The loop queues embedded fields found in 'next', for processing during the next
|
|
// iteration. The multiplicity of the 'current' field counts is recorded
|
|
// in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'.
|
|
for _, scan := range current {
|
|
t := scan.typ
|
|
if visited[t] {
|
|
// We've looked through this type before, at a higher level.
|
|
// That higher level would shadow the lower level we're now at,
|
|
// so this one can't be useful to us. Ignore it.
|
|
continue
|
|
}
|
|
visited[t] = true
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
|
|
exported := (f.PkgPath == "")
|
|
|
|
// If a named field is unexported, ignore it. An anonymous
|
|
// unexported field is processed, because it may contain
|
|
// exported fields, which are visible.
|
|
if !exported && !f.Anonymous {
|
|
continue
|
|
}
|
|
|
|
// Examine the tag.
|
|
tagName, keep, other, err := c.parseTag(f.Tag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !keep {
|
|
continue
|
|
}
|
|
if c.leafTypes(f.Type) {
|
|
fields = append(fields, newField(f, tagName, other, scan.index, i))
|
|
continue
|
|
}
|
|
|
|
var ntyp reflect.Type
|
|
if f.Anonymous {
|
|
// Anonymous field of type T or *T.
|
|
ntyp = f.Type
|
|
if ntyp.Kind() == reflect.Ptr {
|
|
ntyp = ntyp.Elem()
|
|
}
|
|
}
|
|
|
|
// Record fields with a tag name, non-anonymous fields, or
|
|
// anonymous non-struct fields.
|
|
if tagName != "" || ntyp == nil || ntyp.Kind() != reflect.Struct {
|
|
if !exported {
|
|
continue
|
|
}
|
|
fields = append(fields, newField(f, tagName, other, scan.index, i))
|
|
if count[t] > 1 {
|
|
// If there were multiple instances, add a second,
|
|
// so that the annihilation code will see a duplicate.
|
|
fields = append(fields, fields[len(fields)-1])
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Queue embedded struct fields for processing with next level,
|
|
// but only if the embedded types haven't already been queued.
|
|
if nextCount[ntyp] > 0 {
|
|
nextCount[ntyp] = 2 // exact multiple doesn't matter
|
|
continue
|
|
}
|
|
if nextCount == nil {
|
|
nextCount = map[reflect.Type]int{}
|
|
}
|
|
nextCount[ntyp] = 1
|
|
if count[t] > 1 {
|
|
nextCount[ntyp] = 2 // exact multiple doesn't matter
|
|
}
|
|
var index []int
|
|
index = append(index, scan.index...)
|
|
index = append(index, i)
|
|
next = append(next, fieldScan{ntyp, index})
|
|
}
|
|
}
|
|
}
|
|
return fields, nil
|
|
}
|
|
|
|
func newField(f reflect.StructField, tagName string, other interface{}, index []int, i int) Field {
|
|
name := tagName
|
|
if name == "" {
|
|
name = f.Name
|
|
}
|
|
sf := Field{
|
|
Name: name,
|
|
NameFromTag: tagName != "",
|
|
Type: f.Type,
|
|
ParsedTag: other,
|
|
nameBytes: []byte(name),
|
|
}
|
|
sf.equalFold = foldFunc(sf.nameBytes)
|
|
sf.Index = append(sf.Index, index...)
|
|
sf.Index = append(sf.Index, i)
|
|
return sf
|
|
}
|
|
|
|
// byName sorts fields using the following criteria, in order:
|
|
// 1. name
|
|
// 2. embedding depth
|
|
// 3. tag presence (preferring a tagged field)
|
|
// 4. index sequence.
|
|
type byName []Field
|
|
|
|
func (x byName) Len() int { return len(x) }
|
|
|
|
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
|
|
func (x byName) Less(i, j int) bool {
|
|
if x[i].Name != x[j].Name {
|
|
return x[i].Name < x[j].Name
|
|
}
|
|
if len(x[i].Index) != len(x[j].Index) {
|
|
return len(x[i].Index) < len(x[j].Index)
|
|
}
|
|
if x[i].NameFromTag != x[j].NameFromTag {
|
|
return x[i].NameFromTag
|
|
}
|
|
return byIndex(x).Less(i, j)
|
|
}
|
|
|
|
// byIndex sorts field by index sequence.
|
|
type byIndex []Field
|
|
|
|
func (x byIndex) Len() int { return len(x) }
|
|
|
|
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
|
|
func (x byIndex) Less(i, j int) bool {
|
|
xi := x[i].Index
|
|
xj := x[j].Index
|
|
ln := len(xi)
|
|
if l := len(xj); l < ln {
|
|
ln = l
|
|
}
|
|
for k := 0; k < ln; k++ {
|
|
if xi[k] != xj[k] {
|
|
return xi[k] < xj[k]
|
|
}
|
|
}
|
|
return len(xi) < len(xj)
|
|
}
|
|
|
|
// dominantField looks through the fields, all of which are known to have the
|
|
// same name, to find the single field that dominates the others using Go's
|
|
// embedding rules, modified by the presence of tags. If there are multiple
|
|
// top-level fields, the boolean will be false: This condition is an error in
|
|
// Go and we skip all the fields.
|
|
func dominantField(fs []Field) (Field, bool) {
|
|
// The fields are sorted in increasing index-length order, then by presence of tag.
|
|
// That means that the first field is the dominant one. We need only check
|
|
// for error cases: two fields at top level, either both tagged or neither tagged.
|
|
if len(fs) > 1 && len(fs[0].Index) == len(fs[1].Index) && fs[0].NameFromTag == fs[1].NameFromTag {
|
|
return Field{}, false
|
|
}
|
|
return fs[0], true
|
|
}
|
|
|
|
// ParseStandardTag extracts the sub-tag named by key, then parses it using the
|
|
// de facto standard format introduced in encoding/json:
|
|
// "-" means "ignore this tag". It must occur by itself. (parseStandardTag returns an error
|
|
// in this case, whereas encoding/json accepts the "-" even if it is not alone.)
|
|
// "<name>" provides an alternative name for the field
|
|
// "<name>,opt1,opt2,..." specifies options after the name.
|
|
// The options are returned as a []string.
|
|
func ParseStandardTag(key string, t reflect.StructTag) (name string, keep bool, options []string, err error) {
|
|
s := t.Get(key)
|
|
parts := strings.Split(s, ",")
|
|
if parts[0] == "-" {
|
|
if len(parts) > 1 {
|
|
return "", false, nil, errors.New(`"-" field tag with options`)
|
|
}
|
|
return "", false, nil, nil
|
|
}
|
|
if len(parts) > 1 {
|
|
options = parts[1:]
|
|
}
|
|
return parts[0], true, options, nil
|
|
}
|